import numpy as np
import tkinter as tk
from tkinter import ttk, filedialog, messagebox, simpledialog
from PIL import Image, ImageTk, ImageDraw, ImageFont
from sklearn.datasets import fetch_olivetti_faces
from sklearn.model_selection import train_test_split, GridSearchCV, cross_val_score
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier, VotingClassifier, GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA, FastICA
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.metrics import accuracy_score, classification_report, confusion_matrix
from sklearn.feature_selection import SelectKBest, f_classif
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib
# 设置中文字体支持
matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', '等线', 'DejaVu Sans']  # 支持中文显示
matplotlib.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题
import threading
import time
import os
import pickle
from datetime import datetime
from scipy import ndimage, signal
from scipy.spatial.distance import pdist, squareform
from skimage.feature import local_binary_pattern, hog
from skimage.filters import gabor_kernel
from skimage import exposure
import warnings
warnings.filterwarnings('ignore')

class CNNFaceRecognizer:
    def __init__(self):
        self.model = None
        self.pca = None
        self.scaler = None
        self.X_train = None
        self.X_test = None  
        self.y_train = None
        self.y_test = None
        self.X_train_processed = None
        self.X_test_processed = None
        self.is_trained = False
        self.model_type = "SVM"
        
        # 人脸识别数据库
        self.face_database = {}  # {person_id: {'name': 'xxx', 'features': [...]}}
        self.face_counter = 0
        
        self.setup_ui()
        self.load_data()
        
    def setup_ui(self):
        """设置现代化用户界面"""
        self.root = tk.Tk()
        self.root.title("🚀 AI人脸识别系统")
        self.root.geometry("1400x900")
        self.root.configure(bg='#ffffff')  # 白色简约背景
        self.root.resizable(True, True)
        
        # 设置样式
        self.setup_styles()
        
        # 创建主容器
        main_container = tk.Frame(self.root, bg='#ffffff')
        main_container.pack(fill='both', expand=True, padx=20, pady=20)
        
        # 顶部标题栏
        self.create_header(main_container)
        
        # 主内容区域
        content_frame = tk.Frame(main_container, bg='#ffffff')
        content_frame.pack(fill='both', expand=True, pady=(20, 0))
        
        # 左侧面板（控制面板直达底部）
        self.create_left_panel(content_frame)
        
        # 右侧内容区域
        right_content_frame = tk.Frame(content_frame, bg='#ffffff')
        right_content_frame.pack(side='left', fill='both', expand=True, padx=(15, 0))
        
        # 上半部分：中间和右侧面板
        upper_frame = tk.Frame(right_content_frame, bg='#ffffff')
        upper_frame.pack(fill='both', expand=True, pady=(0, 20))
        
        # 中间内容区
        self.create_center_panel(upper_frame)
        
        # 右侧面板
        self.create_right_panel(upper_frame)
        
        # 底部日志面板（调整为右边两部分的宽度）
        self.create_bottom_panel(right_content_frame)
    
    def setup_styles(self):
        """设置白色简约样式"""
        style = ttk.Style()
        
        # 配置主题色彩 - 白色简约风格
        self.colors = {
            'primary': '#0066cc',      # 主色调 - 蓝色
            'secondary': '#f5f5f5',    # 次要色 - 浅灰
            'success': '#28a745',      # 成功色 - 绿色
            'warning': '#ffc107',      # 警告色 - 黄色
            'danger': '#dc3545',       # 危险色 - 红色
            'background': '#ffffff',   # 背景色 - 白色
            'surface': '#f8f9fa',      # 表面色 - 浅灰
            'text': '#212529',         # 文字色 - 深色
            'text_secondary': '#6c757d' # 次要文字色
        }
        
        # 自定义样式
        style.theme_use('clam')
        
        # 配置按钮样式
        style.configure('Modern.TButton',
                       background=self.colors['primary'],
                       foreground='white',
                       borderwidth=0,
                       focuscolor='none',
                       font=('等线', 10, 'bold'),
                       padding=(20, 12))
        
        style.map('Modern.TButton',
                 background=[('active', '#1f6feb'), ('pressed', '#0969da')])
        
        # 配置成功按钮样式
        style.configure('Success.TButton',
                       background=self.colors['success'],
                       foreground='white',
                       borderwidth=0,
                       focuscolor='none',
                       font=('等线', 10, 'bold'),
                       padding=(20, 12))
        
        # 配置警告按钮样式  
        style.configure('Warning.TButton',
                       background=self.colors['warning'],
                       foreground='white',
                       borderwidth=0,
                       focuscolor='none',
                       font=('等线', 10, 'bold'),
                       padding=(20, 12))
        
        # 配置下拉框样式
        style.configure('Modern.TCombobox',
                       fieldbackground=self.colors['surface'],
                       background=self.colors['surface'],
                       foreground=self.colors['text'],
                       borderwidth=1,
                       relief='solid',
                       font=('等线', 10))
        
        # 配置标签框样式
        style.configure('Modern.TLabelframe',
                       background=self.colors['surface'],
                       foreground=self.colors['text'],
                       borderwidth=1,
                       relief='solid',
                       font=('等线', 11, 'bold'))
        
        style.configure('Modern.TLabelframe.Label',
                       background=self.colors['surface'],
                       foreground=self.colors['primary'],
                       font=('等线', 11, 'bold'))
    
    def create_header(self, parent):
        """创建简约顶部标题栏"""
        header_frame = tk.Frame(parent, bg='#f8f9fa', height=80, relief='solid', bd=1)
        header_frame.pack(fill='x', pady=(0, 20))
        header_frame.pack_propagate(False)
        
        # 标题和图标
        title_frame = tk.Frame(header_frame, bg='#f8f9fa')
        title_frame.pack(expand=True)
        
        # 主标题
        title_label = tk.Label(title_frame, 
                              text="🧠 AI人脸识别系统", 
                              font=('等线', 24, 'bold'),
                              bg='#f8f9fa', 
                              fg='#0066cc')
        title_label.pack(pady=(15, 5))
        
        # 副标题
        subtitle_label = tk.Label(title_frame,
                                 text="基于深度学习的高精度人脸识别平台",
                                 font=('等线', 12),
                                 bg='#f8f9fa',
                                 fg='#6c757d')
        subtitle_label.pack()
    
    def create_left_panel(self, parent):
        """创建简约左侧控制面板"""
        left_frame = tk.Frame(parent, bg='#ffffff', width=350)
        left_frame.pack(side='left', fill='both', padx=(0, 0))
        left_frame.pack_propagate(False)
        
        # 控制面板（直达底部）
        control_panel = tk.Frame(left_frame, bg='#f8f9fa', relief='solid', bd=1)
        control_panel.pack(fill='both', expand=True)
        
        # 面板标题
        panel_title = tk.Label(control_panel,
                              text="⚙️ 控制面板",
                              font=('等线', 14, 'bold'),
                              bg='#f8f9fa',
                              fg='#0066cc')
        panel_title.pack(pady=(20, 15))
        
        # 数据信息卡片
        self.create_info_card(control_panel)
        
        # 模型配置区
        self.create_model_config(control_panel)
        
        # 操作按钮区
        self.create_action_buttons(control_panel)
        
        # 状态显示区
        self.create_status_display(control_panel)
    
    def create_info_card(self, parent):
        """创建数据信息卡片"""
        info_card = tk.Frame(parent, bg='#ffffff', relief='solid', bd=1)
        info_card.pack(fill='x', padx=20, pady=(0, 15))
        
        card_title = tk.Label(info_card,
                             text="📊 数据集信息",
                             font=('等线', 11, 'bold'),
                             bg='#ffffff',
                             fg='#0066cc')
        card_title.pack(pady=(10, 5))
        
        self.data_info_label = tk.Label(info_card,
                                       text="数据加载中...",
                                       font=('等线', 9),
                                       bg='#ffffff',
                                       fg='#212529',
                                       justify='left')
        self.data_info_label.pack(pady=(0, 10), padx=10)
    
    def create_model_config(self, parent):
        """创建模型配置区域"""
        config_frame = tk.Frame(parent, bg='#ffffff')
        config_frame.pack(fill='x', padx=20, pady=(0, 15))
        
        # 模型选择
        model_label = tk.Label(config_frame,
                              text="🤖 算法模型",
                              font=('等线', 10, 'bold'),
                              bg='#ffffff',
                              fg='#212529')
        model_label.pack(anchor='w', pady=(0, 5))
        
        self.model_var = tk.StringVar(value="快速随机森林")
        model_combo = ttk.Combobox(config_frame,
                                  textvariable=self.model_var,
                                  values=["快速随机森林", "高级集成模型", "优化SVM", "梯度提升", "投票分类器"],
                                  style='Modern.TCombobox',
                                  state='readonly',
                                  font=('等线', 9))
        model_combo.pack(fill='x', pady=(0, 15))
        
        # 特征提取
        feature_label = tk.Label(config_frame,
                                text="🔍 特征提取",
                                font=('等线', 10, 'bold'),
                                bg='#ffffff',
                                fg='#212529')
        feature_label.pack(anchor='w', pady=(0, 5))
        
        self.feature_var = tk.StringVar(value="快速特征提取")
        feature_combo = ttk.Combobox(config_frame,
                                    textvariable=self.feature_var,
                                    values=["快速特征提取", "LBP特征", "HOG特征", "简化CNN", "原始像素"],
                                    style='Modern.TCombobox',
                                    state='readonly',
                                    font=('等线', 9))
        feature_combo.pack(fill='x')
    
    def create_action_buttons(self, parent):
        """创建操作按钮区域"""
        buttons_frame = tk.Frame(parent, bg='#ffffff')
        buttons_frame.pack(fill='x', padx=20, pady=(15, 0))
        
        # 模型管理按钮行
        model_mgmt_frame = tk.Frame(buttons_frame, bg='#ffffff')
        model_mgmt_frame.pack(fill='x', pady=(0, 10))
        
        # 加载模型按钮
        self.load_button = ttk.Button(model_mgmt_frame,
                                     text="📁 加载模型",
                                     command=self.load_pretrained_model,
                                     style='Modern.TButton',
                                     state='disabled')
        self.load_button.pack(side='left', fill='x', expand=True, padx=(0, 5))
        
        # 模型管理按钮
        self.manage_button = ttk.Button(model_mgmt_frame,
                                       text="🗂️ 管理模型",
                                       command=self.manage_models,
                                       style='Modern.TButton',
                                       state='disabled')
        self.manage_button.pack(side='left', fill='x', expand=True, padx=(5, 0))
        
        # 训练模型按钮
        self.train_button = ttk.Button(buttons_frame,
                                      text="🚀 开始训练",
                                      command=self.train_model_thread,
                                      style='Success.TButton',
                                      state='disabled')
        self.train_button.pack(fill='x', pady=(0, 10))
        
        # 进度条
        self.progress = ttk.Progressbar(buttons_frame,
                                       mode='indeterminate',
                                       style='Modern.Horizontal.TProgressbar')
        self.progress.pack(fill='x', pady=(0, 15))
        
        # 测试和识别按钮 - 两两一行排列
        test_frame = tk.Frame(buttons_frame, bg='#ffffff')
        test_frame.pack(fill='x', pady=(0, 10))
        
        # 第一行：测试模型 和 图片识别
        row1_frame = tk.Frame(test_frame, bg='#ffffff')
        row1_frame.pack(fill='x', pady=(0, 8))
        
        self.test_button = ttk.Button(row1_frame,
                                     text="🧪 测试模型",
                                     command=self.test_model,
                                     style='Modern.TButton',
                                     state='disabled')
        self.test_button.pack(side='left', fill='x', expand=True, padx=(0, 5))
        
        self.select_image_button = ttk.Button(row1_frame,
                                             text="🖼️ 图片识别",
                                             command=self.select_and_predict,
                                             style='Modern.TButton',
                                             state='disabled')
        self.select_image_button.pack(side='left', fill='x', expand=True, padx=(5, 0))
        
        # 第二行：录入人脸 和 人脸管理
        row2_frame = tk.Frame(test_frame, bg='#ffffff')
        row2_frame.pack(fill='x', pady=(0, 10))
        
        self.record_button = ttk.Button(row2_frame,
                                       text="📹 录入人脸",
                                       command=self.record_faces,
                                       style='Warning.TButton')
        self.record_button.pack(side='left', fill='x', expand=True, padx=(0, 5))
        
        self.face_manage_button = ttk.Button(row2_frame,
                                            text="👥 人脸管理",
                                            command=self.manage_faces,
                                            style='Modern.TButton')
        self.face_manage_button.pack(side='left', fill='x', expand=True, padx=(5, 0))
        
        # 第三行：摄像头识别（独占一行）
        self.camera_button = ttk.Button(test_frame,
                                       text="🔍 摄像头识别",
                                       command=self.camera_recognition,
                                       style='Success.TButton',
                                       state='disabled')
        self.camera_button.pack(fill='x')
    
    def create_status_display(self, parent):
        """创建状态显示区域"""
        status_frame = tk.Frame(parent, bg='#ffffff', relief='solid', bd=1)
        status_frame.pack(fill='x', padx=20, pady=(15, 20))
        
        status_title = tk.Label(status_frame,
                               text="📈 系统状态",
                               font=('等线', 11, 'bold'),
                               bg='#ffffff',
                               fg='#0066cc')
        status_title.pack(pady=(10, 5))
        
        self.model_status_label = tk.Label(status_frame,
                                          text="模型状态: 未训练",
                                          font=('等线', 10),
                                          bg='#ffffff',
                                          fg='#6c757d')
        self.model_status_label.pack(pady=(0, 10))
        
    def create_center_panel(self, parent):
        """创建白色简约中间显示面板"""
        center_frame = tk.Frame(parent, bg='#ffffff', width=400)
        center_frame.pack(side='left', fill='both', expand=True, padx=(0, 15))
        
        # 图像显示区域
        display_panel = tk.Frame(center_frame, bg='#f8f9fa', relief='solid', bd=1)
        display_panel.pack(fill='both', expand=True)
        
        # 面板标题
        display_title = tk.Label(display_panel,
                                text="🖼️ 图像显示",
                                font=('等线', 14, 'bold'),
                                bg='#f8f9fa',
                                fg='#0066cc')
        display_title.pack(pady=(20, 15))
        
        # 图像显示画布
        canvas_frame = tk.Frame(display_panel, bg='#ffffff', relief='solid', bd=1)
        canvas_frame.pack(padx=20, pady=(0, 15))
        
        self.image_canvas = tk.Canvas(canvas_frame, 
                                     width=320, 
                                     height=320, 
                                     bg='#ffffff',
                                     highlightthickness=0)
        self.image_canvas.pack(padx=10, pady=10)
        
        # 添加占位文本
        self.image_canvas.create_text(160, 160, 
                                     text="🖼️\n等待图像显示", 
                                     fill='#6c757d', 
                                     font=('等线', 14),
                                     justify='center')
        
        # 识别结果显示
        result_frame = tk.Frame(display_panel, bg='#ffffff', relief='solid', bd=1)
        result_frame.pack(fill='x', padx=20, pady=(0, 20))
        
        result_title = tk.Label(result_frame,
                               text="🎯 识别结果",
                               font=('等线', 11, 'bold'),
                               bg='#ffffff',
                               fg='#0066cc')
        result_title.pack(pady=(10, 5))
        
        self.result_label = tk.Label(result_frame,
                                    text="识别结果将在这里显示",
                                    font=('等线', 11),
                                    bg='#ffffff',
                                    fg='#212529',
                                    wraplength=300,
                                    justify='center')
        self.result_label.pack(pady=(0, 15))
    
    def create_right_panel(self, parent):
        """创建白色简约右侧可视化面板"""
        right_frame = tk.Frame(parent, bg='#ffffff', width=400)
        right_frame.pack(side='right', fill='y')
        right_frame.pack_propagate(False)
        
        # 可视化面板
        viz_panel = tk.Frame(right_frame, bg='#f8f9fa', relief='solid', bd=1)
        viz_panel.pack(fill='both', expand=True)
        
        # 面板标题
        viz_title = tk.Label(viz_panel,
                            text="📊 数据可视化",
                            font=('等线', 14, 'bold'),
                            bg='#f8f9fa',
                            fg='#0066cc')
        viz_title.pack(pady=(20, 15))
        
        # 图表区域
        chart_frame = tk.Frame(viz_panel, bg='#ffffff', relief='solid', bd=1)
        chart_frame.pack(fill='both', expand=True, padx=20, pady=(0, 20))
        
        # 创建matplotlib图形
        self.figure, (self.ax1, self.ax2) = plt.subplots(2, 1, figsize=(5, 7))
        self.figure.patch.set_facecolor('#ffffff')
        
        # 调整子图间距
        self.figure.subplots_adjust(hspace=0.4)  # 增加垂直间距
        
        # 设置白色简约图表样式
        for ax in [self.ax1, self.ax2]:
            ax.set_facecolor('#ffffff')
            ax.tick_params(colors='#212529')
            ax.spines['bottom'].set_color('#dee2e6')
            ax.spines['top'].set_color('#dee2e6')
            ax.spines['right'].set_color('#dee2e6')
            ax.spines['left'].set_color('#dee2e6')
        
        self.canvas_plot = FigureCanvasTkAgg(self.figure, master=chart_frame)
        self.canvas_plot.get_tk_widget().pack(fill='both', expand=True, padx=10, pady=10)
    
    def create_bottom_panel(self, parent):
        """创建白色简约底部日志面板"""
        bottom_frame = tk.Frame(parent, bg='#ffffff', height=150)
        bottom_frame.pack(fill='x')
        bottom_frame.pack_propagate(False)
        
        # 日志面板
        log_panel = tk.Frame(bottom_frame, bg='#f8f9fa', relief='solid', bd=1)
        log_panel.pack(fill='both', expand=True)
        
        # 面板标题
        log_title = tk.Label(log_panel,
                            text="📝 系统日志",
                            font=('等线', 14, 'bold'),
                            bg='#f8f9fa',
                            fg='#0066cc')
        log_title.pack(pady=(15, 10))
        
        # 日志显示区域
        log_frame = tk.Frame(log_panel, bg='#f8f9fa')
        log_frame.pack(fill='both', expand=True, padx=20, pady=(0, 15))
        
        # 创建日志文本框
        self.log_text = tk.Text(log_frame,
                               height=6,
                               bg='#ffffff',
                               fg='#212529',
                               font=('等线', 9),
                               insertbackground='#0066cc',
                               selectbackground='#cfe2ff',
                               relief='solid',
                               bd=1)
        
        # 滚动条
        scrollbar = tk.Scrollbar(log_frame, 
                                orient="vertical", 
                                command=self.log_text.yview,
                                bg='#e9ecef',
                                troughcolor='#f8f9fa')
        self.log_text.configure(yscrollcommand=scrollbar.set)
        
        self.log_text.pack(side='left', fill='both', expand=True)
        scrollbar.pack(side='right', fill='y')
        
    def log_message(self, message, level='INFO'):
        """添加白色简约风格彩色日志消息"""
        timestamp = datetime.now().strftime("%H:%M:%S")
        
        # 根据日志级别设置颜色 - 白色主题配色
        color_map = {
            'INFO': '#0066cc',      # 蓝色
            'SUCCESS': '#28a745',   # 绿色
            'WARNING': '#ffc107',   # 黄色
            'ERROR': '#dc3545',     # 红色
            'DEBUG': '#6c757d'      # 灰色
        }
        
        # 创建彩色标签
        tag_name = f"level_{level.lower()}"
        self.log_text.tag_configure(tag_name, foreground=color_map.get(level, '#212529'))
        
        # 添加日志条目
        time_tag = "timestamp"
        self.log_text.tag_configure(time_tag, foreground='#6c757d')
        
        self.log_text.insert(tk.END, f"[{timestamp}] ", time_tag)
        self.log_text.insert(tk.END, f"[{level}] ", tag_name)
        self.log_text.insert(tk.END, f"{message}\n", "default")
        
        self.log_text.see(tk.END)
        self.root.update_idletasks()
        self.root.update()
        
    def load_data(self):
        """加载Olivetti人脸数据集，并存储到本地目录"""
        try:
            self.log_message("正在加载Olivetti人脸数据集...")
            
            # 创建数据目录
            data_dir = os.path.join(os.getcwd(), "olivetti_faces_data")
            if not os.path.exists(data_dir):
                os.makedirs(data_dir)
            
            # 检查是否已有本地数据
            data_file = os.path.join(data_dir, "olivetti_faces.pkl")
            if os.path.exists(data_file):
                self.log_message("从本地加载数据...")
                with open(data_file, 'rb') as f:
                    data_dict = pickle.load(f)
                    X, y = data_dict['data'], data_dict['target']
            else:
                self.log_message("首次下载数据并保存到本地...")
                faces = fetch_olivetti_faces(data_home=data_dir)
                X, y = faces.data, faces.target
                
                # 保存到本地
                data_dict = {'data': X, 'target': y}
                with open(data_file, 'wb') as f:
                    pickle.dump(data_dict, f)
                
                # 保存原始图像
                images_dir = os.path.join(data_dir, "images")
                if not os.path.exists(images_dir):
                    os.makedirs(images_dir)
                
                for i, img in enumerate(X):
                    img_2d = (img.reshape(64, 64) * 255).astype(np.uint8)
                    Image.fromarray(img_2d).save(os.path.join(images_dir, f"face_{i:03d}_person_{y[i]:02d}.png"))
            
            # 数据预处理和增强
            self.X_raw = X.reshape(-1, 64, 64)
            self.log_message("开始数据增强...")
            X_augmented, y_augmented = self.augment_data(X, y)
            
            # 分割训练和测试数据
            self.X_train, self.X_test, self.y_train, self.y_test = train_test_split(
                X_augmented, y_augmented, test_size=0.15, random_state=42, stratify=y_augmented
            )
            
            # 更新界面信息
            data_info = f"✅ 数据加载完成\n📊 原始样本: {len(X)}\n🔄 增强后样本: {len(X_augmented)}\n🎯 训练样本: {len(self.X_train)}\n🧪 测试样本: {len(self.X_test)}\n🏷️ 类别数: {len(np.unique(y))}\n📁 数据位置: {os.path.basename(data_dir)}"
            self.data_info_label.config(text=data_info)
            self.train_button.config(state='normal')
            self.load_button.config(state='normal')
            self.manage_button.config(state='normal')
            
            # 尝试自动加载预训练模型
            if self.load_model_components():
                self.model_status_label.config(text="模型状态: ✅ 已加载预训练模型")
                self.test_button.config(state='normal')
                self.select_image_button.config(state='normal')
                
            # 加载人脸数据库
            self.load_face_database()
            
            self.log_message("🎉 数据加载成功!", "SUCCESS")
            self.log_message(f"📊 原始数据形状: {X.shape}", "INFO")
            self.log_message(f"🔄 增强后数据形状: {X_augmented.shape}", "INFO")
            self.log_message(f"📁 数据保存位置: {data_dir}", "INFO")
            
            # 显示样本图像
            self.visualize_samples()
            
        except Exception as e:
            self.log_message(f"❌ 数据加载失败: {str(e)}", "ERROR")
            messagebox.showerror("错误", f"数据加载失败: {str(e)}")
    
    def augment_data(self, X, y):
        """数据增强 - 提升数据量和多样性"""
        X_augmented = [X]
        y_augmented = [y]
        
        # 旋转增强
        for angle in [-5, 5]:
            X_rotated = []
            for img in X:
                img_2d = img.reshape(64, 64)
                rotated = ndimage.rotate(img_2d, angle, reshape=False, mode='reflect')
                X_rotated.append(rotated.flatten())
            X_augmented.append(np.array(X_rotated))
            y_augmented.append(y.copy())
        
        # 平移增强
        for shift in [(-2, 0), (2, 0), (0, -2), (0, 2)]:
            X_shifted = []
            for img in X:
                img_2d = img.reshape(64, 64)
                shifted = ndimage.shift(img_2d, shift, mode='reflect')
                X_shifted.append(shifted.flatten())
            X_augmented.append(np.array(X_shifted))
            y_augmented.append(y.copy())
        
        # 噪声增强
        noise_levels = [0.02, 0.05]
        for noise_level in noise_levels:
            X_noisy = []
            for img in X:
                noise = np.random.normal(0, noise_level, img.shape)
                noisy_img = np.clip(img + noise, 0, 1)
                X_noisy.append(noisy_img)
            X_augmented.append(np.array(X_noisy))
            y_augmented.append(y.copy())
        
        # 对比度增强
        for gamma in [0.8, 1.2]:
            X_contrast = []
            for img in X:
                img_2d = img.reshape(64, 64)
                adjusted = exposure.adjust_gamma(img_2d, gamma)
                X_contrast.append(adjusted.flatten())
            X_augmented.append(np.array(X_contrast))
            y_augmented.append(y.copy())
        
        return np.vstack(X_augmented), np.hstack(y_augmented)
    
    def extract_lbp_features(self, img):
        """提取局部二值模式(LBP)特征"""
        img_2d = img.reshape(64, 64)
        # 计算LBP特征
        radius = 3
        n_points = 8 * radius
        lbp = local_binary_pattern(img_2d, n_points, radius, method='uniform')
        # 计算LBP直方图
        n_bins = n_points + 2
        hist, _ = np.histogram(lbp.ravel(), bins=n_bins, range=(0, n_bins))
        # 归一化
        hist = hist.astype(float)
        hist /= (hist.sum() + 1e-6)
        return hist
    
    def extract_hog_features(self, img):
        """提取方向梯度直方图(HOG)特征"""
        img_2d = img.reshape(64, 64)
        # HOG特征参数
        orientations = 9
        pixels_per_cell = (8, 8)
        cells_per_block = (2, 2)
        
        # 计算HOG特征
        features = hog(img_2d, orientations=orientations, 
                      pixels_per_cell=pixels_per_cell,
                      cells_per_block=cells_per_block,
                      visualize=False, feature_vector=True)
        return features
    
    def extract_gabor_features(self, img):
        """提取Gabor滤波器特征"""
        img_2d = img.reshape(64, 64)
        features = []
        
        # 不同频率和方向的Gabor滤波器
        frequencies = [0.1, 0.3, 0.5]
        orientations = [0, 45, 90, 135]
        
        for freq in frequencies:
            for angle in orientations:
                # 创建Gabor滤波器
                kernel = np.real(gabor_kernel(freq, theta=np.deg2rad(angle)))
                # 应用滤波器
                filtered = ndimage.convolve(img_2d, kernel, mode='reflect')
                # 提取统计特征
                features.extend([
                    np.mean(filtered),
                    np.std(filtered),
                    np.mean(np.abs(filtered)),
                    np.max(filtered),
                    np.min(filtered)
                ])
        
        return np.array(features)
    
    def extract_eigenface_features(self, X_train, X_test):
        """提取Eigenfaces特征"""
        # 中心化数据
        X_train_centered = X_train - np.mean(X_train, axis=0)
        X_test_centered = X_test - np.mean(X_train, axis=0)
        
        # 计算协方差矩阵的特征向量
        pca = PCA(n_components=min(200, X_train.shape[0]-1))
        X_train_eigen = pca.fit_transform(X_train_centered)
        X_test_eigen = pca.transform(X_test_centered)
        
        return X_train_eigen, X_test_eigen, pca
    
    def extract_fisherface_features(self, X_train, X_test, y_train):
        """提取Fisherfaces特征（线性判别分析）"""
        from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
        
        # 应用LDA
        lda = LinearDiscriminantAnalysis(n_components=min(39, len(np.unique(y_train))-1))
        X_train_fisher = lda.fit_transform(X_train, y_train)
        X_test_fisher = lda.transform(X_test)
        
        return X_train_fisher, X_test_fisher, lda
    
    def extract_deep_cnn_features(self, X):
        """简化的CNN特征提取 - 优化性能"""
        features = []
        
        for img in X:
            try:
                img_2d = img.reshape(64, 64) if img.shape == (4096,) else img
                
                # 确保图像是2D的
                if len(img_2d.shape) > 2:
                    img_2d = img_2d[:, :, 0] if img_2d.shape[2] == 3 else img_2d.reshape(64, 64)
                
                # 简化特征提取 - 只保留最重要的特征
                # 基础边缘特征
                sobel_x = ndimage.sobel(img_2d, axis=1)
                sobel_y = ndimage.sobel(img_2d, axis=0)
                edges = np.sqrt(sobel_x**2 + sobel_y**2)
                
                # 简单池化
                pooled_img = self.max_pooling(img_2d, pool_size=4)  # 增大池化尺寸减少计算
                
                # 基础统计特征
                basic_features = [
                    np.mean(img_2d), np.std(img_2d), np.max(img_2d), np.min(img_2d),
                    np.mean(edges), np.std(edges)
                ]
                
                # 组合特征 - 大幅简化
                combined_features = np.concatenate([
                    pooled_img.flatten()[:50],  # 减少特征数量
                    basic_features
                ])
                
                features.append(combined_features)
                
            except Exception as e:
                self.log_message(f"深度CNN特征提取失败，使用基础特征: {str(e)}")
                # 如果失败，使用基础特征
                img_2d = img.reshape(64, 64) if img.shape == (4096,) else img
                basic_features = img_2d.flatten()[:100]  # 使用前100个像素作为特征
                # 补齐到相同长度
                padded_features = np.pad(basic_features, (0, max(0, 200 - len(basic_features))), mode='constant')
                features.append(padded_features[:200])
        
        return np.array(features)
    
    def simulate_conv_layer(self, img, num_filters, filter_size):
        """模拟卷积层"""
        # 确保输入是2D图像
        if len(img.shape) > 2:
            # 如果是多维数组，取第一个通道或展平后重塑
            if img.shape[0] < img.shape[1]:  # 假设第一维是通道数
                img = img[0]  # 取第一个通道
            else:
                img = img.reshape(64, 64)  # 强制重塑为64x64
        
        # 创建多个卷积核
        kernels = []
        
        # 边缘检测卷积核
        sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]])
        sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]])
        
        # 高斯模糊卷积核
        gaussian = np.array([[1, 2, 1], [2, 4, 2], [1, 2, 1]]) / 16
        
        # 锐化卷积核
        sharpen = np.array([[0, -1, 0], [-1, 5, -1], [0, -1, 0]])
        
        kernels.extend([sobel_x, sobel_y, gaussian, sharpen])
        
        # 随机卷积核
        for _ in range(min(num_filters - 4, 8)):  # 限制卷积核数量
            kernel = np.random.randn(filter_size, filter_size) * 0.1
            kernels.append(kernel)
        
        # 应用卷积和激活
        feature_maps = []
        for i, kernel in enumerate(kernels[:min(num_filters, 12)]):  # 限制最大数量
            try:
                conv_result = ndimage.convolve(img, kernel, mode='reflect')
                activated = np.maximum(0, conv_result)  # ReLU激活
                pooled = self.max_pooling(activated, pool_size=2)  # 最大池化
                feature_maps.append(pooled)
            except Exception as e:
                # 如果某个卷积失败，跳过
                self.log_message(f"卷积 {i} 失败，跳过: {str(e)}")
                continue
        
        if len(feature_maps) == 0:
            # 如果所有卷积都失败，返回简单特征
            return img[:16, :16].reshape(1, 16, 16)  # 返回图像左上角作为特征
        
        return np.array(feature_maps)
    
    def max_pooling(self, img, pool_size):
        """最大池化操作"""
        h, w = img.shape
        pooled_h = h // pool_size
        pooled_w = w // pool_size
        
        pooled = np.zeros((pooled_h, pooled_w))
        for i in range(pooled_h):
            for j in range(pooled_w):
                pooled[i, j] = np.max(img[i*pool_size:(i+1)*pool_size, 
                                         j*pool_size:(j+1)*pool_size])
        return pooled
    
    def extract_global_features(self, img):
        """提取全局统计特征"""
        features = []
        
        # 基本统计特征
        features.extend([
            np.mean(img), np.std(img), np.var(img),
            np.min(img), np.max(img), np.median(img),
            np.percentile(img, 25), np.percentile(img, 75)
        ])
        
        # 梯度特征
        grad_x = ndimage.sobel(img, axis=1)
        grad_y = ndimage.sobel(img, axis=0)
        gradient_magnitude = np.sqrt(grad_x**2 + grad_y**2)
        
        features.extend([
            np.mean(gradient_magnitude),
            np.std(gradient_magnitude),
            np.max(gradient_magnitude)
        ])
        
        # 边缘密度
        edges = gradient_magnitude > np.mean(gradient_magnitude)
        edge_density = np.sum(edges) / img.size
        features.append(edge_density)
        
        return np.array(features)
    
    def extract_texture_features(self, img):
        """提取纹理特征"""
        features = []
        
        # 灰度共生矩阵特征
        # 计算不同方向的灰度差异
        directions = [(1, 0), (0, 1), (1, 1), (1, -1)]
        
        for dx, dy in directions:
            # 计算灰度差异
            shifted = np.roll(np.roll(img, dx, axis=0), dy, axis=1)
            diff = np.abs(img - shifted)
            
            features.extend([
                np.mean(diff),
                np.std(diff),
                np.max(diff)
            ])
        
        # 局部方差
        local_variance = ndimage.generic_filter(img, np.var, size=3)
        features.extend([
            np.mean(local_variance),
            np.std(local_variance)
        ])
        
        return np.array(features)
    
    def average_pooling(self, img, pool_size):
        """平均池化操作"""
        h, w = img.shape
        pooled_h = h // pool_size
        pooled_w = w // pool_size
        
        pooled = np.zeros((pooled_h, pooled_w))
        for i in range(pooled_h):
            for j in range(pooled_w):
                pooled[i, j] = np.mean(img[i*pool_size:(i+1)*pool_size, 
                                          j*pool_size:(j+1)*pool_size])
        return pooled
    
    def extract_features(self, X_train, X_test):
        """根据选择的方法提取特征"""
        feature_method = self.feature_var.get()
        
        if feature_method == "快速特征提取":
            self.log_message("使用快速特征提取方法...")
            
            X_train_features = []
            X_test_features = []
            
            # 训练集特征提取 - 简化版本
            for i, img in enumerate(X_train):
                if i % 1000 == 0:  # 进一步减少日志输出频率
                    self.log_message(f"处理训练样本 {i}/{len(X_train)}", "INFO")
                    if i % 2000 == 0:  # 只在特定时候更新界面
                        self.root.update()
                
                # 只提取最基础的特征
                img_2d = img.reshape(64, 64)
                
                # 基础统计特征
                basic_feat = [
                    np.mean(img_2d), np.std(img_2d), np.max(img_2d), np.min(img_2d),
                    np.median(img_2d), np.var(img_2d)
                ]
                
                # 简单的边缘特征
                edges = ndimage.sobel(img_2d)
                edge_feat = [np.mean(edges), np.std(edges)]
                
                # 降采样特征 (16x16 = 256维)
                downsampled = img_2d[::4, ::4].flatten()
                
                # 组合特征
                combined_feat = np.concatenate([basic_feat, edge_feat, downsampled])
                X_train_features.append(combined_feat)
            
            # 测试集特征提取
            for i, img in enumerate(X_test):
                if i % 200 == 0:
                    self.log_message(f"处理测试样本 {i}/{len(X_test)}", "INFO")
                    if i % 400 == 0:  # 减少界面更新频率
                        self.root.update()
                
                img_2d = img.reshape(64, 64)
                basic_feat = [
                    np.mean(img_2d), np.std(img_2d), np.max(img_2d), np.min(img_2d),
                    np.median(img_2d), np.var(img_2d)
                ]
                edges = ndimage.sobel(img_2d)
                edge_feat = [np.mean(edges), np.std(edges)]
                downsampled = img_2d[::4, ::4].flatten()
                
                combined_feat = np.concatenate([basic_feat, edge_feat, downsampled])
                X_test_features.append(combined_feat)
            
            X_train_combined = np.array(X_train_features)
            X_test_combined = np.array(X_test_features)
            
            # 简化的标准化处理
            self.log_message("应用标准化处理...", "INFO")
            self.scaler = StandardScaler()
            X_train_processed = self.scaler.fit_transform(X_train_combined)
            X_test_processed = self.scaler.transform(X_test_combined)
            
        elif feature_method == "高级混合特征":
            self.log_message("提取LBP+HOG+Gabor+CNN混合特征...")
            
            # 提取多种特征并组合
            X_train_features = []
            X_test_features = []
            
            for i, img in enumerate(X_train):
                if i % 100 == 0:
                    self.log_message(f"处理训练样本 {i}/{len(X_train)}")
                
                # LBP特征
                lbp_feat = self.extract_lbp_features(img)
                # HOG特征
                hog_feat = self.extract_hog_features(img)
                # Gabor特征
                gabor_feat = self.extract_gabor_features(img)
                # 深度CNN特征
                cnn_feat = self.extract_deep_cnn_features([img])[0]
                
                # 组合所有特征，确保长度一致
                try:
                    combined = np.concatenate([lbp_feat, hog_feat, gabor_feat, cnn_feat])
                    X_train_features.append(combined)
                except Exception as e:
                    self.log_message(f"特征组合失败，样本 {i}: {str(e)}")
                    # 使用基础特征作为后备
                    basic_feat = np.concatenate([lbp_feat, hog_feat])
                    # 补齐到固定长度
                    padded_feat = np.pad(basic_feat, (0, max(0, 500 - len(basic_feat))), mode='constant')
                    X_train_features.append(padded_feat[:500])
            
            for i, img in enumerate(X_test):
                if i % 20 == 0:
                    self.log_message(f"处理测试样本 {i}/{len(X_test)}")
                
                lbp_feat = self.extract_lbp_features(img)
                hog_feat = self.extract_hog_features(img)
                gabor_feat = self.extract_gabor_features(img)
                cnn_feat = self.extract_deep_cnn_features([img])[0]
                
                try:
                    combined = np.concatenate([lbp_feat, hog_feat, gabor_feat, cnn_feat])
                    X_test_features.append(combined)
                except Exception as e:
                    self.log_message(f"测试特征组合失败，样本 {i}: {str(e)}")
                    # 使用基础特征作为后备
                    basic_feat = np.concatenate([lbp_feat, hog_feat])
                    # 补齐到固定长度
                    padded_feat = np.pad(basic_feat, (0, max(0, 500 - len(basic_feat))), mode='constant')
                    X_test_features.append(padded_feat[:500])
            
            X_train_combined = np.array(X_train_features)
            X_test_combined = np.array(X_test_features)
            
            # 特征选择
            self.log_message("应用特征选择...")
            selector = SelectKBest(f_classif, k=min(500, X_train_combined.shape[1]))
            X_train_selected = selector.fit_transform(X_train_combined, self.y_train)
            X_test_selected = selector.transform(X_test_combined)
            
            # 标准化
            self.scaler = StandardScaler()
            X_train_scaled = self.scaler.fit_transform(X_train_selected)
            X_test_scaled = self.scaler.transform(X_test_selected)
            
            # PCA降维
            self.pca = PCA(n_components=min(200, X_train_scaled.shape[1], X_train_scaled.shape[0]-1))
            X_train_processed = self.pca.fit_transform(X_train_scaled)
            X_test_processed = self.pca.transform(X_test_scaled)
            
            self.feature_selector = selector
            
        elif feature_method == "LBP+HOG+Gabor":
            self.log_message("提取LBP+HOG+Gabor特征...")
            
            X_train_features = []
            X_test_features = []
            
            for img in X_train:
                lbp_feat = self.extract_lbp_features(img)
                hog_feat = self.extract_hog_features(img)
                gabor_feat = self.extract_gabor_features(img)
                combined = np.concatenate([lbp_feat, hog_feat, gabor_feat])
                X_train_features.append(combined)
            
            for img in X_test:
                lbp_feat = self.extract_lbp_features(img)
                hog_feat = self.extract_hog_features(img)
                gabor_feat = self.extract_gabor_features(img)
                combined = np.concatenate([lbp_feat, hog_feat, gabor_feat])
                X_test_features.append(combined)
            
            X_train_processed = np.array(X_train_features)
            X_test_processed = np.array(X_test_features)
            
        elif feature_method == "深度CNN模拟":
            self.log_message("提取深度CNN模拟特征...")
            
            X_train_processed = self.extract_deep_cnn_features(X_train)
            X_test_processed = self.extract_deep_cnn_features(X_test)
            
            # 标准化
            self.scaler = StandardScaler()
            X_train_processed = self.scaler.fit_transform(X_train_processed)
            X_test_processed = self.scaler.transform(X_test_processed)
            
        elif feature_method == "Eigenfaces+Fisherfaces":
            self.log_message("提取Eigenfaces+Fisherfaces特征...")
            
            # Eigenfaces特征
            X_train_eigen, X_test_eigen, pca_eigen = self.extract_eigenface_features(X_train, X_test)
            
            # Fisherfaces特征
            X_train_fisher, X_test_fisher, lda_fisher = self.extract_fisherface_features(
                X_train_eigen, X_test_eigen, self.y_train)
            
            # 组合特征
            X_train_processed = np.concatenate([X_train_eigen, X_train_fisher], axis=1)
            X_test_processed = np.concatenate([X_test_eigen, X_test_fisher], axis=1)
            
            self.pca = pca_eigen
            self.lda = lda_fisher
            
        else:  # 原始像素
            X_train_processed = X_train
            X_test_processed = X_test
            
        return X_train_processed, X_test_processed
    
    def create_model(self):
        """根据选择创建高级模型"""
        model_type = self.model_var.get()
        
        if model_type == "快速随机森林":
            self.log_message("使用快速随机森林模型...")
            return RandomForestClassifier(
                n_estimators=50,  # 减少树的数量
                max_depth=8,      # 限制深度
                min_samples_split=20,
                min_samples_leaf=10,
                bootstrap=True,
                random_state=42,
                n_jobs=-1  # 使用所有CPU核心
            )
            
        elif model_type == "高级集成模型":
            self.log_message("初始化集成模型组件...")
            # 创建多个基础模型（优化参数以提高训练速度）
            svm_model = SVC(kernel='rbf', C=1.0, gamma='scale', probability=True, random_state=42)
            self.log_message("- SVM组件已初始化")
            
            rf_model = RandomForestClassifier(n_estimators=100, max_depth=10, min_samples_split=10, 
                                            min_samples_leaf=5, random_state=42, n_jobs=-1)
            self.log_message("- 随机森林组件已初始化")
            
            gb_model = GradientBoostingClassifier(n_estimators=50, learning_rate=0.1, 
                                                max_depth=4, random_state=42)
            self.log_message("- 梯度提升组件已初始化")
            
            lr_model = LogisticRegression(C=1.0, max_iter=500, random_state=42, n_jobs=-1)
            self.log_message("- 逻辑回归组件已初始化")
            
            # 创建投票集成分类器
            ensemble = VotingClassifier(
                estimators=[
                    ('svm', svm_model),
                    ('rf', rf_model), 
                    ('gb', gb_model),
                    ('lr', lr_model)
                ],
                voting='soft',  # 使用概率投票
                n_jobs=1  # 避免多进程冲突
            )
            self.log_message("集成模型创建完成，开始训练...")
            return ensemble
            
        elif model_type == "优化SVM":
            # 使用网格搜索优化的SVM
            from sklearn.model_selection import GridSearchCV
            
            svm = SVC(probability=True, random_state=42)
            param_grid = {
                'C': [1, 10, 100],
                'gamma': ['scale', 'auto', 0.001, 0.01, 0.1],
                'kernel': ['rbf', 'poly']
            }
            
            grid_search = GridSearchCV(svm, param_grid, cv=5, scoring='accuracy', n_jobs=-1)
            return grid_search
            
        elif model_type == "梯度提升":
            return GradientBoostingClassifier(
                n_estimators=200, 
                learning_rate=0.1,
                max_depth=6,
                min_samples_split=20,
                min_samples_leaf=10,
                subsample=0.8,
                random_state=42
            )
            
        elif model_type == "投票分类器":
            # 不同类型的基础分类器
            clf1 = SVC(kernel='rbf', C=10, probability=True, random_state=42)
            clf2 = RandomForestClassifier(n_estimators=150, max_depth=12, random_state=42)
            clf3 = GradientBoostingClassifier(n_estimators=100, random_state=42)
            
            return VotingClassifier(
                estimators=[('svm', clf1), ('rf', clf2), ('gb', clf3)],
                voting='soft'
            )
            
        else:  # 默认快速随机森林
            return RandomForestClassifier(
                n_estimators=50, 
                max_depth=8,
                min_samples_split=20,
                min_samples_leaf=10,
                bootstrap=True,
                random_state=42,
                n_jobs=-1
            )
    
    def train_model_thread(self):
        """在线程中训练模型"""
        thread = threading.Thread(target=self.train_model)
        thread.daemon = True
        thread.start()
        
    def train_model(self):
        """训练模型"""
        try:
            self.log_message("开始训练模型...")
            self.progress.start()
            self.train_button.config(state='disabled')
            self.model_status_label.config(text="模型状态: 训练中...")
            
            # 特征提取
            self.log_message(f"应用特征提取方法: {self.feature_var.get()}")
            self.X_train_processed, self.X_test_processed = self.extract_features(
                self.X_train, self.X_test
            )
            
            # 创建和训练模型
            self.model = self.create_model()
            self.log_message(f"使用模型: {self.model_var.get()}")
            
            # 增加训练进度反馈
            self.log_message("开始模型训练...")
            self.log_message(f"训练数据形状: {self.X_train_processed.shape}")
            self.log_message(f"训练样本数量: {len(self.y_train)}")
            
            start_time = time.time()
            self.model.fit(self.X_train_processed, self.y_train)
            training_time = time.time() - start_time
            
            self.log_message(f"模型训练完成，耗时: {training_time:.2f}秒")
            
            # 详细评估模型
            self.log_message("评估模型性能...")
            
            # 交叉验证
            cv_scores = cross_val_score(self.model, self.X_train_processed, self.y_train, cv=5)
            self.log_message(f"交叉验证准确率: {cv_scores.mean():.4f} (+/- {cv_scores.std() * 2:.4f})")
            
            # 训练和测试预测
            train_pred = self.model.predict(self.X_train_processed)
            test_pred = self.model.predict(self.X_test_processed)
            
            train_accuracy = accuracy_score(self.y_train, train_pred)
            test_accuracy = accuracy_score(self.y_test, test_pred)
            
            # 详细分类报告
            self.log_message("生成详细分类报告...")
            test_report = classification_report(self.y_test, test_pred, output_dict=True)
            
            # 计算每类准确率
            per_class_accuracy = []
            for class_id in np.unique(self.y_test):
                class_mask = self.y_test == class_id
                if np.sum(class_mask) > 0:
                    class_accuracy = accuracy_score(self.y_test[class_mask], test_pred[class_mask])
                    per_class_accuracy.append(class_accuracy)
            
            avg_per_class_accuracy = np.mean(per_class_accuracy)
            
            # 混淆矩阵
            conf_matrix = confusion_matrix(self.y_test, test_pred)
            
            self.log_message(f"平均每类准确率: {avg_per_class_accuracy:.4f}")
            self.log_message(f"宏平均F1分数: {test_report['macro avg']['f1-score']:.4f}")
            self.log_message(f"加权平均F1分数: {test_report['weighted avg']['f1-score']:.4f}")
            
            # 保存模型和相关组件
            self.save_model_components()
            
            self.progress.stop()
            self.is_trained = True
            self.model_status_label.config(text=f"模型状态: 已训练 (准确率: {test_accuracy:.3f})")
            self.test_button.config(state='normal')
            self.select_image_button.config(state='normal')
            self.camera_button.config(state='normal')
            self.train_button.config(state='normal')
            
            self.log_message(f"模型训练完成!")
            self.log_message(f"训练时间: {training_time:.2f}秒")
            self.log_message(f"训练准确率: {train_accuracy:.3f}")
            self.log_message(f"测试准确率: {test_accuracy:.3f}")
            
            # 更新可视化
            self.update_visualization(train_accuracy, test_accuracy, avg_per_class_accuracy)
            
            result_msg = f"模型训练完成!\n测试准确率: {test_accuracy:.4f}\n交叉验证准确率: {cv_scores.mean():.4f}\n每类平均准确率: {avg_per_class_accuracy:.4f}"
            messagebox.showinfo("成功", result_msg)
            
        except Exception as e:
            self.progress.stop()
            self.train_button.config(state='normal')
            self.model_status_label.config(text="模型状态: 训练失败")
            self.log_message(f"模型训练失败: {str(e)}")
            messagebox.showerror("错误", f"模型训练失败: {str(e)}")
    
    def save_model_components(self):
        """保存模型和相关组件到以时间+算法名命名的文件夹"""
        try:
            # 创建主要的模型目录
            base_model_dir = os.path.join(os.getcwd(), "trained_models")
            if not os.path.exists(base_model_dir):
                os.makedirs(base_model_dir)
            
            # 生成时间戳和算法名
            timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
            model_name = self.model_var.get().replace(" ", "_")
            feature_name = self.feature_var.get().replace(" ", "_")
            
            # 创建具体的模型目录
            model_dir_name = f"{timestamp}_{model_name}_{feature_name}"
            model_dir = os.path.join(base_model_dir, model_dir_name)
            os.makedirs(model_dir, exist_ok=True)
            
            # 保存模型
            model_file = os.path.join(model_dir, "face_recognition_model.pkl")
            with open(model_file, 'wb') as f:
                pickle.dump(self.model, f)
            
            # 保存特征处理器
            if hasattr(self, 'scaler') and self.scaler is not None:
                scaler_file = os.path.join(model_dir, "scaler.pkl")
                with open(scaler_file, 'wb') as f:
                    pickle.dump(self.scaler, f)
            
            if hasattr(self, 'pca') and self.pca is not None:
                pca_file = os.path.join(model_dir, "pca.pkl")
                with open(pca_file, 'wb') as f:
                    pickle.dump(self.pca, f)
            
            if hasattr(self, 'feature_selector') and self.feature_selector is not None:
                selector_file = os.path.join(model_dir, "feature_selector.pkl")
                with open(selector_file, 'wb') as f:
                    pickle.dump(self.feature_selector, f)
            
            # 获取模型性能数据
            train_accuracy = getattr(self, 'train_accuracy', 0)
            test_accuracy = getattr(self, 'test_accuracy', 0)
            
            # 如果还没有计算，尝试获取
            if train_accuracy == 0 and hasattr(self, 'model') and hasattr(self, 'X_train_processed'):
                try:
                    train_pred = self.model.predict(self.X_train_processed)
                    train_accuracy = accuracy_score(self.y_train, train_pred)
                    self.train_accuracy = train_accuracy
                except:
                    pass
            
            if test_accuracy == 0 and hasattr(self, 'model') and hasattr(self, 'X_test_processed'):
                try:
                    test_pred = self.model.predict(self.X_test_processed)
                    test_accuracy = accuracy_score(self.y_test, test_pred)
                    self.test_accuracy = test_accuracy
                except:
                    pass
            
            # 保存模型配置和性能信息
            config = {
                'model_type': self.model_var.get(),
                'feature_method': self.feature_var.get(),
                'train_accuracy': train_accuracy,
                'test_accuracy': test_accuracy,
                'has_feature_selector': hasattr(self, 'feature_selector'),
                'has_pca': hasattr(self, 'pca'),
                'has_scaler': hasattr(self, 'scaler'),
                'training_time': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
                'model_dir_name': model_dir_name
            }
            
            config_file = os.path.join(model_dir, "model_config.pkl")
            with open(config_file, 'wb') as f:
                pickle.dump(config, f)
            
            # 保存一个可读的性能报告
            report_file = os.path.join(model_dir, "performance_report.txt")
            with open(report_file, 'w', encoding='utf-8') as f:
                f.write(f"模型训练报告\n")
                f.write(f"=" * 30 + "\n")
                f.write(f"训练时间: {config['training_time']}\n")
                f.write(f"算法模型: {config['model_type']}\n")
                f.write(f"特征提取: {config['feature_method']}\n")
                f.write(f"训练准确率: {train_accuracy:.4f}\n")
                f.write(f"测试准确率: {test_accuracy:.4f}\n")
                f.write(f"模型目录: {model_dir_name}\n")
                f.write(f"包含组件:\n")
                f.write(f"  - 主模型: 是\n")
                f.write(f"  - 标准化器: {'是' if config['has_scaler'] else '否'}\n")
                f.write(f"  - PCA降维: {'是' if config['has_pca'] else '否'}\n")
                f.write(f"  - 特征选择: {'是' if config['has_feature_selector'] else '否'}\n")
            
            self.log_message(f"模型已保存到: {model_dir}", "SUCCESS")
            self.log_message(f"模型目录名: {model_dir_name}", "INFO")
            
        except Exception as e:
            self.log_message(f"模型保存失败: {str(e)}", "ERROR")
    
    def get_available_models(self):
        """获取所有可用的模型列表"""
        try:
            base_model_dir = os.path.join(os.getcwd(), "trained_models")
            if not os.path.exists(base_model_dir):
                return []
            
            available_models = []
            for model_folder in os.listdir(base_model_dir):
                model_path = os.path.join(base_model_dir, model_folder)
                if os.path.isdir(model_path):
                    # 检查是否包含必要的模型文件
                    model_file = os.path.join(model_path, "face_recognition_model.pkl")
                    config_file = os.path.join(model_path, "model_config.pkl")
                    
                    if os.path.exists(model_file) and os.path.exists(config_file):
                        # 读取配置信息
                        try:
                            with open(config_file, 'rb') as f:
                                config = pickle.load(f)
                            
                            model_info = {
                                'folder_name': model_folder,
                                'path': model_path,
                                'config': config,
                                'display_name': f"{model_folder} - {config.get('model_type', 'Unknown')} - 测试准确率: {config.get('test_accuracy', 0):.3f}"
                            }
                            available_models.append(model_info)
                        except Exception as e:
                            self.log_message(f"读取模型配置失败 {model_folder}: {str(e)}", "WARNING")
            
            # 按时间戳排序（最新的在前）
            available_models.sort(key=lambda x: x['folder_name'], reverse=True)
            return available_models
            
        except Exception as e:
            self.log_message(f"获取模型列表失败: {str(e)}", "ERROR")
            return []
    
    def select_model_dialog(self):
        """显示模型选择对话框"""
        available_models = self.get_available_models()
        
        if not available_models:
            messagebox.showwarning("警告", "没有找到任何已训练的模型！")
            return None
        
        # 创建模型选择窗口
        selection_window = tk.Toplevel(self.root)
        selection_window.title("选择要加载的模型")
        selection_window.geometry("800x500")
        selection_window.configure(bg='#ffffff')
        selection_window.transient(self.root)
        selection_window.grab_set()
        
        # 居中显示
        selection_window.update_idletasks()
        x = (selection_window.winfo_screenwidth() // 2) - (800 // 2)
        y = (selection_window.winfo_screenheight() // 2) - (500 // 2)
        selection_window.geometry(f"800x500+{x}+{y}")
        
        selected_model = None
        
        # 标题
        title_label = tk.Label(selection_window, 
                              text="🤖 选择要加载的模型", 
                              font=('等线', 16, 'bold'),
                              bg='#ffffff', 
                              fg='#0066cc')
        title_label.pack(pady=20)
        
        # 创建列表框和滚动条的框架
        list_frame = tk.Frame(selection_window, bg='#ffffff')
        list_frame.pack(fill='both', expand=True, padx=20, pady=(0, 20))
        
        # 创建列表框
        listbox = tk.Listbox(list_frame, 
                            font=('等线', 10),
                            bg='#f8f9fa',
                            fg='#212529',
                            selectbackground='#0066cc',
                            selectforeground='white',
                            height=15)
        
        # 创建滚动条
        scrollbar = tk.Scrollbar(list_frame, orient="vertical", command=listbox.yview)
        listbox.configure(yscrollcommand=scrollbar.set)
        
        # 布局列表框和滚动条
        listbox.pack(side='left', fill='both', expand=True)
        scrollbar.pack(side='right', fill='y')
        
        # 添加模型到列表
        for model_info in available_models:
            listbox.insert(tk.END, model_info['display_name'])
        
        # 详细信息显示区域
        info_frame = tk.Frame(selection_window, bg='#f8f9fa', relief='solid', bd=1)
        info_frame.pack(fill='x', padx=20, pady=(0, 10))
        
        info_label = tk.Label(info_frame,
                             text="选择一个模型查看详细信息",
                             font=('等线', 10),
                             bg='#f8f9fa',
                             fg='#6c757d',
                             justify='left',
                             wraplength=750)
        info_label.pack(pady=10, padx=10)
        
        def on_select(event):
            """当选择项改变时显示详细信息"""
            try:
                selection = event.widget.curselection()
                if selection:
                    index = selection[0]
                    model_info = available_models[index]
                    config = model_info['config']
                    
                    info_text = f"📁 文件夹: {model_info['folder_name']}\n"
                    info_text += f"🤖 算法: {config.get('model_type', 'Unknown')}\n"
                    info_text += f"🔍 特征提取: {config.get('feature_method', 'Unknown')}\n"
                    info_text += f"🎯 训练准确率: {config.get('train_accuracy', 0):.4f}\n"
                    info_text += f"📊 测试准确率: {config.get('test_accuracy', 0):.4f}\n"
                    info_text += f"⏰ 训练时间: {config.get('training_time', 'Unknown')}\n"
                    info_text += f"📦 包含组件: "
                    components = []
                    if config.get('has_scaler'): components.append("标准化器")
                    if config.get('has_pca'): components.append("PCA降维")
                    if config.get('has_feature_selector'): components.append("特征选择")
                    info_text += ", ".join(components) if components else "仅基础模型"
                    
                    info_label.config(text=info_text, fg='#212529')
            except Exception as e:
                info_label.config(text=f"显示详细信息时出错: {str(e)}", fg='#dc3545')
        
        listbox.bind('<<ListboxSelect>>', on_select)
        
        # 按钮框架
        button_frame = tk.Frame(selection_window, bg='#ffffff')
        button_frame.pack(pady=10)
        
        def on_load():
            nonlocal selected_model
            selection = listbox.curselection()
            if selection:
                selected_model = available_models[selection[0]]
                selection_window.destroy()
            else:
                messagebox.showwarning("警告", "请选择一个模型！")
        
        def on_cancel():
            selection_window.destroy()
        
        # 加载按钮
        load_btn = ttk.Button(button_frame,
                             text="🚀 加载选中模型",
                             command=on_load,
                             style='Success.TButton')
        load_btn.pack(side='left', padx=(0, 10))
        
        # 取消按钮
        cancel_btn = ttk.Button(button_frame,
                               text="❌ 取消",
                               command=on_cancel,
                               style='Modern.TButton')
        cancel_btn.pack(side='left')
        
        # 等待窗口关闭
        selection_window.wait_window()
        return selected_model
    
    def load_model_components(self, model_path=None):
        """加载已保存的模型和组件"""
        try:
            if model_path is None:
                # 尝试自动加载最新的模型
                available_models = self.get_available_models()
                if not available_models:
                    return False
                model_path = available_models[0]['path']  # 使用最新的模型
                self.log_message(f"自动加载最新模型: {available_models[0]['folder_name']}", "INFO")
            
            # 检查模型文件是否存在
            model_file = os.path.join(model_path, "face_recognition_model.pkl")
            if not os.path.exists(model_file):
                self.log_message(f"模型文件不存在: {model_file}", "ERROR")
                return False
            
            # 加载模型
            with open(model_file, 'rb') as f:
                self.model = pickle.load(f)
            self.log_message("主模型加载成功", "SUCCESS")
            
            # 重置特征处理器
            self.scaler = None
            self.pca = None
            self.feature_selector = None
            
            # 加载特征处理器
            scaler_file = os.path.join(model_path, "scaler.pkl")
            if os.path.exists(scaler_file):
                with open(scaler_file, 'rb') as f:
                    self.scaler = pickle.load(f)
                self.log_message("标准化器加载成功", "SUCCESS")
            
            pca_file = os.path.join(model_path, "pca.pkl")
            if os.path.exists(pca_file):
                with open(pca_file, 'rb') as f:
                    self.pca = pickle.load(f)
                self.log_message("PCA降维器加载成功", "SUCCESS")
            
            selector_file = os.path.join(model_path, "feature_selector.pkl")
            if os.path.exists(selector_file):
                with open(selector_file, 'rb') as f:
                    self.feature_selector = pickle.load(f)
                self.log_message("特征选择器加载成功", "SUCCESS")
            
            # 加载配置
            config_file = os.path.join(model_path, "model_config.pkl")
            if os.path.exists(config_file):
                with open(config_file, 'rb') as f:
                    config = pickle.load(f)
                    self.model_var.set(config.get('model_type', '快速随机森林'))
                    self.feature_var.set(config.get('feature_method', '快速特征提取'))
                    
                    # 更新界面显示准确率
                    test_accuracy = config.get('test_accuracy', 0)
                    model_name = config.get('model_dir_name', os.path.basename(model_path))
                    self.model_status_label.config(text=f"模型状态: ✅ {model_name} (准确率: {test_accuracy:.3f})")
                    
                self.log_message("模型配置加载成功", "SUCCESS")
            
            self.is_trained = True
            self.log_message("预训练模型加载完成！", "SUCCESS")
            return True
            
        except Exception as e:
            self.log_message(f"模型加载失败: {str(e)}", "ERROR")
            return False
    
    def load_pretrained_model(self):
        """手动加载预训练模型 - 让用户选择"""
        try:
            # 显示模型选择对话框
            selected_model = self.select_model_dialog()
            
            if selected_model is None:
                self.log_message("用户取消了模型选择", "INFO")
                return
            
            # 加载选中的模型
            self.log_message(f"用户选择加载模型: {selected_model['folder_name']}", "INFO")
            
            if self.load_model_components(selected_model['path']):
                self.test_button.config(state='normal')
                self.select_image_button.config(state='normal')
                self.camera_button.config(state='normal')
                
                # 显示成功信息
                config = selected_model['config']
                success_msg = f"模型加载成功！\n\n"
                success_msg += f"📁 模型: {selected_model['folder_name']}\n"
                success_msg += f"🤖 算法: {config.get('model_type', 'Unknown')}\n"
                success_msg += f"🔍 特征提取: {config.get('feature_method', 'Unknown')}\n"
                success_msg += f"📊 测试准确率: {config.get('test_accuracy', 0):.4f}\n"
                success_msg += f"⏰ 训练时间: {config.get('training_time', 'Unknown')}"
                
                messagebox.showinfo("成功", success_msg)
                self.log_message("预训练模型加载成功！", "SUCCESS")
            else:
                messagebox.showerror("错误", f"加载模型失败：{selected_model['folder_name']}")
                self.log_message(f"模型加载失败: {selected_model['folder_name']}", "ERROR")
                
        except Exception as e:
            self.log_message(f"手动加载模型失败: {str(e)}", "ERROR")
            messagebox.showerror("错误", f"加载模型时出错: {str(e)}")
    
    def manage_models(self):
        """模型管理界面"""
        try:
            available_models = self.get_available_models()
            
            if not available_models:
                messagebox.showinfo("信息", "没有找到任何已训练的模型！")
                return
            
            # 创建模型管理窗口
            mgmt_window = tk.Toplevel(self.root)
            mgmt_window.title("模型管理")
            mgmt_window.geometry("900x600")
            mgmt_window.configure(bg='#ffffff')
            mgmt_window.transient(self.root)
            mgmt_window.grab_set()
            
            # 居中显示
            mgmt_window.update_idletasks()
            x = (mgmt_window.winfo_screenwidth() // 2) - (900 // 2)
            y = (mgmt_window.winfo_screenheight() // 2) - (600 // 2)
            mgmt_window.geometry(f"900x600+{x}+{y}")
            
            # 标题
            title_label = tk.Label(mgmt_window, 
                                  text="🗂️ 模型管理中心", 
                                  font=('等线', 16, 'bold'),
                                  bg='#ffffff', 
                                  fg='#0066cc')
            title_label.pack(pady=20)
            
            # 统计信息
            stats_frame = tk.Frame(mgmt_window, bg='#f8f9fa', relief='solid', bd=1)
            stats_frame.pack(fill='x', padx=20, pady=(0, 20))
            
            total_models = len(available_models)
            total_size = 0
            for model_info in available_models:
                model_path = model_info['path']
                for file in os.listdir(model_path):
                    file_path = os.path.join(model_path, file)
                    if os.path.isfile(file_path):
                        total_size += os.path.getsize(file_path)
            
            size_mb = total_size / (1024 * 1024)
            
            stats_text = f"📊 统计信息: 共 {total_models} 个模型，占用空间 {size_mb:.1f} MB"
            stats_label = tk.Label(stats_frame,
                                  text=stats_text,
                                  font=('等线', 12),
                                  bg='#f8f9fa',
                                  fg='#212529')
            stats_label.pack(pady=10)
            
            # 创建主框架
            main_frame = tk.Frame(mgmt_window, bg='#ffffff')
            main_frame.pack(fill='both', expand=True, padx=20, pady=(0, 20))
            
            # 左侧列表
            left_frame = tk.Frame(main_frame, bg='#ffffff')
            left_frame.pack(side='left', fill='both', expand=True, padx=(0, 15))
            
            list_title = tk.Label(left_frame,
                                 text="📋 模型列表",
                                 font=('等线', 12, 'bold'),
                                 bg='#ffffff',
                                 fg='#0066cc')
            list_title.pack(anchor='w', pady=(0, 10))
            
            # 创建Treeview
            columns = ('时间', '算法', '特征', '准确率')
            tree = ttk.Treeview(left_frame, columns=columns, show='headings', height=15)
            
            # 设置列标题
            tree.heading('#1', text='训练时间')
            tree.heading('#2', text='算法模型')
            tree.heading('#3', text='特征提取')
            tree.heading('#4', text='测试准确率')
            
            # 设置列宽
            tree.column('#1', width=150, minwidth=120)
            tree.column('#2', width=120, minwidth=100)
            tree.column('#3', width=120, minwidth=100)
            tree.column('#4', width=100, minwidth=80)
            
            # 添加滚动条
            tree_scroll = ttk.Scrollbar(left_frame, orient="vertical", command=tree.yview)
            tree.configure(yscrollcommand=tree_scroll.set)
            
            tree.pack(side='left', fill='both', expand=True)
            tree_scroll.pack(side='right', fill='y')
            
            # 右侧详情和操作
            right_frame = tk.Frame(main_frame, bg='#f8f9fa', relief='solid', bd=1, width=300)
            right_frame.pack(side='right', fill='y')
            right_frame.pack_propagate(False)
            
            details_title = tk.Label(right_frame,
                                    text="📄 详细信息",
                                    font=('等线', 12, 'bold'),
                                    bg='#f8f9fa',
                                    fg='#0066cc')
            details_title.pack(pady=(15, 10))
            
            # 详情显示区域
            details_text = tk.Text(right_frame,
                                  height=15,
                                  width=35,
                                  bg='#ffffff',
                                  fg='#212529',
                                  font=('等线', 9),
                                  relief='solid',
                                  bd=1,
                                  state='disabled')
            details_text.pack(padx=15, pady=(0, 15))
            
            # 操作按钮
            button_frame = tk.Frame(right_frame, bg='#f8f9fa')
            button_frame.pack(fill='x', padx=15, pady=(0, 15))
            
            load_selected_btn = ttk.Button(button_frame,
                                          text="🚀 加载选中模型",
                                          style='Success.TButton')
            load_selected_btn.pack(fill='x', pady=(0, 8))
            
            delete_btn = ttk.Button(button_frame,
                                   text="🗑️ 删除选中模型",
                                   style='Warning.TButton')
            delete_btn.pack(fill='x', pady=(0, 8))
            
            export_btn = ttk.Button(button_frame,
                                   text="📤 导出模型信息",
                                   style='Modern.TButton')
            export_btn.pack(fill='x')
            
            # 填充数据到Treeview
            model_data = {}
            for model_info in available_models:
                config = model_info['config']
                training_time = config.get('training_time', 'Unknown')
                if training_time != 'Unknown':
                    # 只显示日期和时间，去掉秒
                    try:
                        dt = datetime.strptime(training_time, "%Y-%m-%d %H:%M:%S")
                        display_time = dt.strftime("%m-%d %H:%M")
                    except:
                        display_time = training_time
                else:
                    display_time = model_info['folder_name'][:16]  # 使用文件夹名的前16个字符
                
                item_id = tree.insert('', 'end', values=(
                    display_time,
                    config.get('model_type', 'Unknown')[:12],
                    config.get('feature_method', 'Unknown')[:12],
                    f"{config.get('test_accuracy', 0):.3f}"
                ))
                model_data[item_id] = model_info
            
            # 选择事件处理
            def on_item_select(event):
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    model_info = model_data[item_id]
                    config = model_info['config']
                    
                    # 更新详细信息
                    details_text.config(state='normal')
                    details_text.delete(1.0, tk.END)
                    
                    detail_info = f"📁 文件夹名称:\n{model_info['folder_name']}\n\n"
                    detail_info += f"🤖 算法模型:\n{config.get('model_type', 'Unknown')}\n\n"
                    detail_info += f"🔍 特征提取方法:\n{config.get('feature_method', 'Unknown')}\n\n"
                    detail_info += f"📊 性能指标:\n"
                    detail_info += f"  训练准确率: {config.get('train_accuracy', 0):.4f}\n"
                    detail_info += f"  测试准确率: {config.get('test_accuracy', 0):.4f}\n\n"
                    detail_info += f"⏰ 训练时间:\n{config.get('training_time', 'Unknown')}\n\n"
                    
                    # 计算模型大小
                    model_size = 0
                    for file in os.listdir(model_info['path']):
                        file_path = os.path.join(model_info['path'], file)
                        if os.path.isfile(file_path):
                            model_size += os.path.getsize(file_path)
                    
                    detail_info += f"💾 模型大小: {model_size / (1024*1024):.1f} MB\n\n"
                    
                    # 包含的组件
                    detail_info += f"📦 包含组件:\n"
                    detail_info += f"  主模型: ✓\n"
                    if config.get('has_scaler'): detail_info += f"  标准化器: ✓\n"
                    if config.get('has_pca'): detail_info += f"  PCA降维: ✓\n"
                    if config.get('has_feature_selector'): detail_info += f"  特征选择: ✓\n"
                    
                    details_text.insert(1.0, detail_info)
                    details_text.config(state='disabled')
            
            tree.bind('<<TreeviewSelect>>', on_item_select)
            
            # 按钮事件处理
            def load_selected():
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    model_info = model_data[item_id]
                    mgmt_window.destroy()
                    
                    if self.load_model_components(model_info['path']):
                        self.test_button.config(state='normal')
                        self.select_image_button.config(state='normal')
                        self.camera_button.config(state='normal')
                        messagebox.showinfo("成功", f"已加载模型: {model_info['folder_name']}")
                    else:
                        messagebox.showerror("错误", "模型加载失败！")
                else:
                    messagebox.showwarning("警告", "请选择一个模型！")
            
            def delete_selected():
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    model_info = model_data[item_id]
                    
                    result = messagebox.askyesno("确认删除", 
                        f"确定要删除模型吗？\n\n模型: {model_info['folder_name']}\n\n此操作不可撤销！")
                    
                    if result:
                        try:
                            import shutil
                            shutil.rmtree(model_info['path'])
                            tree.delete(item_id)
                            del model_data[item_id]
                            messagebox.showinfo("成功", "模型已删除！")
                            self.log_message(f"已删除模型: {model_info['folder_name']}", "INFO")
                            
                            # 清空详情显示
                            details_text.config(state='normal')
                            details_text.delete(1.0, tk.END)
                            details_text.config(state='disabled')
                            
                        except Exception as e:
                            messagebox.showerror("错误", f"删除模型失败: {str(e)}")
                            self.log_message(f"删除模型失败: {str(e)}", "ERROR")
                else:
                    messagebox.showwarning("警告", "请选择一个模型！")
            
            def export_info():
                try:
                    export_file = filedialog.asksaveasfilename(
                        title="导出模型信息",
                        defaultextension=".txt",
                        filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
                    )
                    
                    if export_file:
                        with open(export_file, 'w', encoding='utf-8') as f:
                            f.write("AI人脸识别系统 - 模型信息导出\n")
                            f.write("=" * 50 + "\n\n")
                            f.write(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                            f.write(f"模型总数: {len(available_models)}\n\n")
                            
                            for i, model_info in enumerate(available_models, 1):
                                config = model_info['config']
                                f.write(f"模型 {i}:\n")
                                f.write(f"  文件夹: {model_info['folder_name']}\n")
                                f.write(f"  算法: {config.get('model_type', 'Unknown')}\n")
                                f.write(f"  特征提取: {config.get('feature_method', 'Unknown')}\n")
                                f.write(f"  训练准确率: {config.get('train_accuracy', 0):.4f}\n")
                                f.write(f"  测试准确率: {config.get('test_accuracy', 0):.4f}\n")
                                f.write(f"  训练时间: {config.get('training_time', 'Unknown')}\n")
                                f.write("\n")
                        
                        messagebox.showinfo("成功", f"模型信息已导出到: {export_file}")
                        
                except Exception as e:
                    messagebox.showerror("错误", f"导出失败: {str(e)}")
            
            # 绑定按钮事件
            load_selected_btn.config(command=load_selected)
            delete_btn.config(command=delete_selected)
            export_btn.config(command=export_info)
            
        except Exception as e:
            self.log_message(f"模型管理界面出错: {str(e)}", "ERROR")
            messagebox.showerror("错误", f"打开模型管理界面失败: {str(e)}")
    
    def visualize_samples(self):
        """可视化样本数据"""
        self.ax1.clear()
        
        # 显示几个样本图像
        fig_samples = plt.figure(figsize=(6, 4))
        for i in range(8):
            plt.subplot(2, 4, i+1)
            plt.imshow(self.X_raw[i*50], cmap='gray')
            plt.title(f'Person {i}')
            plt.axis('off')
        
        plt.tight_layout()
        
        # 在ax1中显示样本分布
        self.ax1.hist(self.y_train, bins=40, alpha=0.7, color='blue')
        self.ax1.set_title('训练数据分布')
        self.ax1.set_xlabel('人员ID')
        self.ax1.set_ylabel('样本数量')
        
        # 保持子图间距设置
        self.figure.subplots_adjust(hspace=0.4)
        self.canvas_plot.draw()
    
    def update_visualization(self, train_acc, test_acc, per_class_acc):
        """更新可视化图表"""
        self.ax2.clear()
        
        # 准确率对比
        models = ['训练集', '测试集', '平均每类']
        accuracies = [train_acc, test_acc, per_class_acc]
        colors = ['blue', 'orange', 'green']
        
        bars = self.ax2.bar(models, accuracies, color=colors, alpha=0.7)
        self.ax2.set_title(f'{self.model_var.get()} 模型性能')
        self.ax2.set_ylabel('准确率')
        self.ax2.set_ylim(0, 1)
        
        # 添加数值标签
        for bar, acc in zip(bars, accuracies):
            height = bar.get_height()
            color = 'red' if acc < 0.95 else 'black'  # 低于95%用红色显示
            self.ax2.text(bar.get_x() + bar.get_width()/2., height + 0.01,
                         f'{acc:.4f}', ha='center', va='bottom', color=color, fontweight='bold')
        
        # 添加95%基准线
        self.ax2.axhline(y=0.95, color='red', linestyle='--', alpha=0.7, label='95%目标')
        self.ax2.legend()
        
        # 保持子图间距设置
        self.figure.subplots_adjust(hspace=0.4)
        self.canvas_plot.draw()
    
    def test_model(self):
        """测试模型性能"""
        if not self.is_trained:
            messagebox.showwarning("警告", "请先训练模型!")
            return
            
        if not hasattr(self, 'X_test_processed') or self.X_test_processed is None:
            messagebox.showwarning("警告", "测试数据未处理，请重新训练模型!")
            return
            
        try:
            # 随机选择测试图像
            idx = np.random.randint(0, len(self.X_test))
            test_image = self.X_test[idx]
            true_label = self.y_test[idx]
            
            # 预测
            if hasattr(self.model, 'predict_proba'):
                probabilities = self.model.predict_proba([self.X_test_processed[idx]])
                predicted_label = np.argmax(probabilities)
                confidence = np.max(probabilities)
            else:
                predicted_label = self.model.predict([self.X_test_processed[idx]])[0]
                confidence = 1.0
            
            # 显示图像
            self.display_image(test_image.reshape(64, 64))
            
            # 显示结果
            result_text = f"真实标签: 人员 {true_label}\n预测标签: 人员 {predicted_label}\n置信度: {confidence:.3f}"
            if predicted_label == true_label:
                result_text += "\n识别正确! ✓"
                self.result_label.config(foreground='green')
            else:
                result_text += "\n识别错误! ✗"
                self.result_label.config(foreground='red')
                
            self.result_label.config(text=result_text)
            self.log_message(f"测试结果: {result_text.replace(chr(10), ' ')}")
            
        except Exception as e:
            self.log_message(f"测试失败: {str(e)}")
            messagebox.showerror("错误", f"测试失败: {str(e)}")
    
    def select_and_predict(self):
        """选择图片进行识别 - 使用与测试功能相同的算法，基于olivetti_faces_data数据库"""
        # 检查是否有训练好的模型（图片识别必须使用训练模型）
        if not (hasattr(self, 'model') and self.model is not None and self.is_trained):
            messagebox.showwarning("警告", "请先训练模型！\n\n图片识别功能需要使用训练好的机器学习模型\n与测试功能使用完全相同的算法")
            return
            
        try:
            file_path = filedialog.askopenfilename(
                title="选择图片",
                filetypes=[("图片文件", "*.jpg *.jpeg *.png *.bmp *.tiff")]
            )
            
            if file_path:
                # 加载和预处理图像
                try:
                    # 尝试使用OpenCV读取
                    image = cv2.imread(file_path, cv2.IMREAD_GRAYSCALE)
                    if image is None:
                        # 如果OpenCV失败，尝试使用PIL
                        pil_image = Image.open(file_path).convert('L')
                        image = np.array(pil_image)
                    
                    if image is None or image.size == 0:
                        messagebox.showerror("错误", "无法读取图片文件，请检查文件格式!")
                        return
                        
                    self.log_message(f"成功加载图片: {file_path}")
                    
                except Exception as e:
                    messagebox.showerror("错误", f"读取图片文件失败: {str(e)}")
                    self.log_message(f"图片读取错误: {str(e)}", "ERROR")
                    return
                
                try:
                    image_resized = cv2.resize(image, (64, 64))
                    image_normalized = image_resized.astype('float64') / 255.0
                except Exception as e:
                    messagebox.showerror("错误", f"图片预处理失败: {str(e)}")
                    self.log_message(f"图片预处理错误: {str(e)}", "ERROR")
                    return
                
                # 特征提取
                try:
                    # 始终使用与训练时相同的特征提取方法
                    processed_image = self.extract_face_features(image_normalized.flatten())
                    self.log_message(f"使用特征提取方法: {self.feature_var.get()}", "INFO")
                    self.log_message(f"提取的特征向量形状: {processed_image.shape}", "DEBUG")
                    self.log_message(f"特征向量前5个值: {processed_image[:5]}", "DEBUG")
                except Exception as e:
                    messagebox.showerror("错误", f"特征提取失败: {str(e)}")
                    self.log_message(f"特征提取错误: {str(e)}", "ERROR")
                    return
                
                # 使用与测试功能完全相同的算法进行识别
                try:
                    recognition_result = self.recognize_face_for_image(image_normalized.flatten())
                    
                    # 尝试从文件名提取真实标签（如果是数据集图片）
                    true_label = None
                    is_dataset_image = False
                    try:
                        filename = os.path.basename(file_path)
                        if 'person_' in filename:
                            # 提取真实标签，格式如：face_032_person_03.png
                            import re
                            match = re.search(r'person_(\d+)', filename)
                            if match:
                                true_label = int(match.group(1))
                                is_dataset_image = True
                    except:
                        pass
                    
                    # 构建显示文本
                    if recognition_result['success']:
                        name = recognition_result['name']
                        confidence = recognition_result['confidence']
                        method = recognition_result['method']
                        predicted_label = recognition_result.get('predicted_label', 'Unknown')
                        
                        if is_dataset_image and true_label is not None:
                            # 数据集图片，显示完整信息
                            result_text = f"真实标签: 人员 {true_label}\n"
                            result_text += f"识别结果: {name}\n"
                            result_text += f"置信度: {confidence:.3f}\n"
                            result_text += f"识别方法: {method}\n"
                            
                            # 检查预测标签是否正确
                            if predicted_label == true_label:
                                result_text += "识别正确! ✓"
                                self.result_label.config(text=result_text, foreground='green')
                            else:
                                result_text += "识别结果不匹配! ✗"
                                self.result_label.config(text=result_text, foreground='orange')
                        else:
                            # 外部图片，显示识别结果
                            result_text = f"识别结果: {name}\n置信度: {confidence:.3f}\n识别方法: {method}"
                            self.result_label.config(text=result_text, foreground='green')
                        
                        self.log_message(f"图片识别成功: {name}, 置信度: {confidence:.3f}, 方法: {method}")
                    else:
                        # 识别失败
                        confidence = recognition_result['confidence']
                        method = recognition_result['method']
                        error = recognition_result.get('error', '')
                        
                        if is_dataset_image and true_label is not None:
                            result_text = f"真实标签: 人员 {true_label}\n"
                            result_text += f"识别结果: 未知人员\n"
                            result_text += f"最高置信度: {confidence:.3f}\n"
                            result_text += f"识别方法: {method}"
                        else:
                            result_text = f"识别结果: 未知人员\n最高置信度: {confidence:.3f}\n识别方法: {method}"
                        
                        if error:
                            result_text += f"\n错误: {error}"
                        
                        self.result_label.config(text=result_text, foreground='red')
                        self.log_message(f"图片识别失败: 未知人员, 置信度: {confidence:.3f}, 方法: {method}")
                        
                        if error:
                            self.log_message(f"识别错误: {error}", "ERROR")
                    
                    # 显示图像
                    try:
                        self.display_image(image_resized)
                        self.log_message("图像显示成功", "SUCCESS")
                    except Exception as e:
                        self.log_message(f"图像显示失败: {str(e)}", "ERROR")
                        
                except Exception as e:
                    result_text = f"识别过程出错\n{str(e)}"
                    self.result_label.config(text=result_text, foreground='red')
                    self.log_message(f"识别过程错误: {str(e)}", "ERROR")
                
        except Exception as e:
            self.log_message(f"图片识别失败: {str(e)}")
            messagebox.showerror("错误", f"图片识别失败: {str(e)}")
    
    def load_face_cascade(self):
        """安全地加载人脸级联分类器"""
        try:
            # 方法1: 尝试从OpenCV数据目录加载
            try:
                cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
                if os.path.exists(cascade_path):
                    face_cascade = cv2.CascadeClassifier(cascade_path)
                    if not face_cascade.empty():
                        self.log_message("成功从OpenCV默认路径加载级联分类器", "SUCCESS")
                        return face_cascade
            except Exception as e:
                self.log_message(f"默认路径加载失败: {str(e)}", "WARNING")
            
            # 方法2: 尝试从本地工作目录加载
            local_path = "haarcascade_frontalface_default.xml"
            if os.path.exists(local_path):
                try:
                    face_cascade = cv2.CascadeClassifier(local_path)
                    if not face_cascade.empty():
                        self.log_message("成功从本地文件加载级联分类器", "SUCCESS")
                        return face_cascade
                except Exception as e:
                    self.log_message(f"本地文件加载失败: {str(e)}", "WARNING")
            
            # 方法3: 下载级联分类器文件
            self.log_message("尝试下载级联分类器...", "INFO")
            try:
                import urllib.request
                import ssl
                
                # 创建SSL上下文以处理HTTPS
                ssl_context = ssl.create_default_context()
                ssl_context.check_hostname = False
                ssl_context.verify_mode = ssl.CERT_NONE
                
                url = "https://raw.githubusercontent.com/opencv/opencv/master/data/haarcascades/haarcascade_frontalface_default.xml"
                
                # 下载文件
                req = urllib.request.Request(url)
                with urllib.request.urlopen(req, context=ssl_context, timeout=30) as response:
                    with open(local_path, 'wb') as f:
                        f.write(response.read())
                
                self.log_message("级联分类器下载完成", "SUCCESS")
                
                # 验证下载的文件
                face_cascade = cv2.CascadeClassifier(local_path)
                if not face_cascade.empty():
                    self.log_message("下载的级联分类器验证成功", "SUCCESS")
                    return face_cascade
                else:
                    self.log_message("下载的文件无效", "ERROR")
                    
            except Exception as e:
                self.log_message(f"下载级联分类器失败: {str(e)}", "ERROR")
            
            # 方法4: 创建备用的简单人脸检测
            self.log_message("所有方法都失败，将使用备用检测方案", "WARNING")
            return None
                
        except Exception as e:
            self.log_message(f"加载人脸检测器时发生未知错误: {str(e)}", "ERROR")
            return None
    
    def record_faces(self):
        """录入人脸到数据库 - 使用当前选择的特征提取方法"""
        try:
            cap = cv2.VideoCapture(0)
            if not cap.isOpened():
                messagebox.showerror("错误", "无法打开摄像头!")
                return
            
            # 显示当前使用的特征提取方法
            feature_method = self.feature_var.get()
            model_method = self.model_var.get()
            
            info_msg = f"人脸录入设置:\n"
            info_msg += f"- 特征提取方法: {feature_method}\n"
            info_msg += f"- 算法模型: {model_method}\n"
            if hasattr(self, 'model') and self.model is not None:
                info_msg += f"- 已加载训练模型: 是\n"
            else:
                info_msg += f"- 已加载训练模型: 否\n"
            info_msg += f"\n操作说明:\n- 按 's' 键保存当前检测到的人脸\n- 按 'q' 键退出录入模式"
            
            self.log_message(f"开始人脸录入，使用 {feature_method} 特征提取")
            messagebox.showinfo("人脸录入", info_msg)
            
            face_cascade = self.load_face_cascade()
            use_simple_mode = False
            if face_cascade is None:
                # 如果没有级联分类器，使用简单的备用方案
                result = messagebox.askyesno("检测器缺失", 
                    "人脸检测器加载失败，是否继续使用简单录入模式？\n(将使用摄像头中心区域作为人脸)")
                if not result:
                    cap.release()
                    return
                use_simple_mode = True
                self.log_message("使用简单录入模式", "WARNING")
            
            while True:
                ret, frame = cap.read()
                if not ret:
                    self.log_message("无法读取摄像头画面", "ERROR")
                    break
                    
                # 转换为灰度图
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                
                if use_simple_mode:
                    # 简单模式：使用中心区域作为人脸
                    h, w = gray.shape
                    center_size = min(h, w) // 3
                    x = (w - center_size) // 2
                    y = (h - center_size) // 2
                    
                    cv2.rectangle(frame, (x, y), (x+center_size, y+center_size), (0, 255, 0), 2)
                    cv2.putText(frame, 'Simple Mode - Center Area', (x, y-10), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                    cv2.putText(frame, 'Press S to Save', (x, y+center_size+25), 
                               cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                    faces = [(x, y, center_size, center_size)]
                else:
                    # 正常模式：使用级联分类器检测人脸，添加质量筛选
                    try:
                        raw_faces = face_cascade.detectMultiScale(
                            gray, 
                            scaleFactor=1.05,      # 降低缩放因子，提高精度
                            minNeighbors=6,        # 增加最小邻居数，减少误检
                            minSize=(40, 40),      # 设置最小人脸大小
                            maxSize=(300, 300),    # 设置最大人脸大小
                            flags=cv2.CASCADE_SCALE_IMAGE
                        )
                        
                        # 对检测到的人脸进行质量筛选
                        faces = self.filter_face_detections(gray, raw_faces)
                        
                        # 绘制人脸框
                        for (x, y, w, h) in faces:
                            cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
                            cv2.putText(frame, 'Press S to Save', (x, y-10), 
                                       cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
                    except Exception as e:
                        self.log_message(f"人脸检测错误: {str(e)}", "WARNING")
                        faces = []
                
                cv2.imshow('人脸录入 - 按S保存，按Q退出', frame)
                
                key = cv2.waitKey(1) & 0xFF
                if key == ord('q'):
                    break
                elif key == ord('s') and len(faces) > 0:
                    # 获取第一个检测到的人脸
                    x, y, w, h = faces[0]
                    face_roi = gray[y:y+h, x:x+w]
                    
                    # 询问姓名
                    cap.release()
                    cv2.destroyAllWindows()
                    
                    name = simpledialog.askstring("录入人脸", "请输入这个人的姓名:")
                    if name and name.strip():
                        # 处理人脸特征
                        face_resized = cv2.resize(face_roi, (64, 64))
                        face_normalized = face_resized.astype('float64') / 255.0
                        
                        # 提取特征（使用当前选择的特征提取方法）
                        features = self.extract_face_features(face_normalized.flatten())
                        
                        # 调试信息
                        self.log_message(f"录入人脸特征提取完成，特征维度: {features.shape}")
                        self.log_message(f"使用特征提取方法: {self.feature_var.get()}")
                        
                        # 保存到数据库
                        person_id = f"person_{self.face_counter}"
                        self.face_database[person_id] = {
                            'name': name.strip(),
                            'features': features,
                            'image': face_resized,
                            'feature_method': self.feature_var.get(),  # 记录特征提取方法
                            'model_method': self.model_var.get(),      # 记录模型方法
                            'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
                        }
                        self.face_counter += 1
                        
                        success_msg = f"已成功录入 {name} 的人脸信息！\n"
                        success_msg += f"特征提取方法: {self.feature_var.get()}\n"
                        success_msg += f"特征维度: {features.shape[0]}\n"
                        if hasattr(self, 'model') and self.model is not None:
                            success_msg += f"已加载训练模型: 是"
                        else:
                            success_msg += f"已加载训练模型: 否\n(建议先训练模型以获得更好的识别效果)"
                        
                        self.log_message(f"成功录入人脸: {name}")
                        messagebox.showinfo("录入成功", success_msg)
                        
                        # 保存数据库到文件
                        self.save_face_database()
                        
                        # 启用识别功能
                        self.camera_button.config(state='normal')
                        
                        # 重新打开摄像头继续录入
                        cap = cv2.VideoCapture(0)
                    else:
                        # 重新打开摄像头
                        cap = cv2.VideoCapture(0)
                        
            cap.release()
            cv2.destroyAllWindows()
            self.log_message(f"人脸录入结束，当前数据库中有 {len(self.face_database)} 个人脸")
            
        except Exception as e:
            self.log_message(f"人脸录入失败: {str(e)}")
            messagebox.showerror("错误", f"人脸录入失败: {str(e)}")
        finally:
            # 确保资源释放
            try:
                cv2.destroyAllWindows()
            except:
                pass
    
    def filter_face_detections(self, gray_img, raw_faces):
        """对检测到的人脸进行质量筛选，过滤掉明显的误检（背景、墙壁、物品等）"""
        if len(raw_faces) == 0:
            return []
        
        filtered_faces = []
        img_height, img_width = gray_img.shape
        
        for (x, y, w, h) in raw_faces:
            # 基本尺寸检查
            if w < 40 or h < 40 or w > 300 or h > 300:
                continue
            
            # 宽高比检查 - 人脸通常接近正方形或略高
            aspect_ratio = w / h
            if aspect_ratio < 0.7 or aspect_ratio > 1.4:
                continue
            
            # 边界检查 - 确保人脸区域完全在图像内
            if x < 0 or y < 0 or x + w > img_width or y + h > img_height:
                continue
            
            # 位置检查 - 过滤明显不合理的位置（如图像边角的小区域）
            center_x = x + w // 2
            center_y = y + h // 2
            
            # 排除过于靠近边界的检测（通常是误检）
            margin = min(img_width, img_width) * 0.08  # 8%的边界
            if (center_x < margin or center_x > img_width - margin or
                center_y < margin or center_y > img_height - margin):
                # 只有当检测区域很小时才排除边界区域
                if w < 80 or h < 80:
                    continue
            
            # 人脸区域质量检查
            try:
                face_roi = gray_img[y:y+h, x:x+w]
                
                # 检查方差 - 人脸区域应该有足够的纹理变化（排除纯色墙壁等）
                face_variance = np.var(face_roi)
                if face_variance < 150:  # 方差太低可能是纯色区域
                    continue
                
                # 检查边缘密度 - 人脸应该有较多的边缘特征
                edges = cv2.Canny(face_roi, 50, 150)
                edge_density = np.sum(edges > 0) / (w * h)
                if edge_density < 0.03:  # 边缘密度太低
                    continue
                
                # 检查亮度分布 - 避免过亮或过暗的区域
                mean_brightness = np.mean(face_roi)
                if mean_brightness < 30 or mean_brightness > 220:
                    continue
                
                # 检查亮度标准差 - 人脸应该有一定的亮度变化
                brightness_std = np.std(face_roi)
                if brightness_std < 15:  # 亮度变化太小可能是均匀背景
                    continue
                
                # 检查局部特征 - 使用简单的人脸特征检测
                # 将人脸分成9个区域，检查亮度分布
                face_h, face_w = face_roi.shape
                region_h, region_w = face_h // 3, face_w // 3
                
                # 计算不同区域的平均亮度
                regions = []
                for i in range(3):
                    for j in range(3):
                        start_y = i * region_h
                        end_y = min((i + 1) * region_h, face_h)
                        start_x = j * region_w
                        end_x = min((j + 1) * region_w, face_w)
                        
                        region = face_roi[start_y:end_y, start_x:end_x]
                        regions.append(np.mean(region))
                
                # 检查区域间的亮度差异（人脸通常有明暗变化）
                region_std = np.std(regions)
                if region_std < 8:  # 区域间差异太小
                    continue
                
                # 通过所有检查的人脸
                filtered_faces.append((x, y, w, h))
                
            except Exception as e:
                # 如果质量检查失败，保守地排除这个检测
                self.log_message(f"人脸质量检查失败，跳过: {str(e)}", "WARNING")
                continue
        
        # 如果所有人脸都被过滤掉，且原始检测数量不多，可能过滤过严
        if len(filtered_faces) == 0 and len(raw_faces) <= 2:
            # 选择最大的检测作为备选，但要符合基本要求
            for face in sorted(raw_faces, key=lambda f: f[2] * f[3], reverse=True):
                x, y, w, h = face
                if (w >= 50 and h >= 50 and w <= 250 and h <= 250 and
                    0.8 <= w/h <= 1.3):
                    filtered_faces.append(face)
                    self.log_message("使用备选人脸检测（降低质量要求）", "INFO")
                    break
        
        if len(raw_faces) > 0 and len(filtered_faces) == 0:
            self.log_message(f"过滤掉了所有 {len(raw_faces)} 个检测结果（可能是背景误检）", "INFO")
        elif len(filtered_faces) < len(raw_faces):
            self.log_message(f"过滤掉了 {len(raw_faces) - len(filtered_faces)} 个误检结果", "INFO")
        
        return filtered_faces
    
    def extract_face_features(self, face_data):
        """提取人脸特征 - 使用与训练时相同的方法"""
        try:
            # 将输入数据重塑为正确格式
            if len(face_data.shape) == 1:
                face_data = face_data.reshape(1, -1)
            elif len(face_data.shape) == 2 and face_data.shape[0] > 1:
                face_data = face_data.reshape(1, -1)
            
            # 使用与训练时相同的特征提取方法
            feature_method = self.feature_var.get()
            
            if feature_method == "快速特征提取":
                # 使用与训练时相同的快速特征提取
                img = face_data.flatten()
                img_2d = img.reshape(64, 64)
                
                # 基础统计特征
                basic_feat = [
                    np.mean(img_2d), np.std(img_2d), np.max(img_2d), np.min(img_2d),
                    np.median(img_2d), np.var(img_2d)
                ]
                
                # 简单的边缘特征
                edges = ndimage.sobel(img_2d)
                edge_feat = [np.mean(edges), np.std(edges)]
                
                # 降采样特征 (16x16 = 256维)
                downsampled = img_2d[::4, ::4].flatten()
                
                # 组合特征
                combined_feat = np.concatenate([basic_feat, edge_feat, downsampled])
                
                # 标准化
                if hasattr(self, 'scaler') and self.scaler is not None:
                    features_scaled = self.scaler.transform([combined_feat])
                    return features_scaled[0]
                else:
                    return combined_feat
                    
            elif feature_method == "高级混合特征":
                # 使用高级混合特征提取
                img = face_data.flatten()
                
                # LBP特征
                lbp_feat = self.extract_lbp_features(img)
                # HOG特征
                hog_feat = self.extract_hog_features(img)
                # Gabor特征
                gabor_feat = self.extract_gabor_features(img)
                # 深度CNN特征
                cnn_feat = self.extract_deep_cnn_features([img])[0]
                
                # 组合所有特征
                try:
                    combined = np.concatenate([lbp_feat, hog_feat, gabor_feat, cnn_feat])
                except:
                    # 如果组合失败，使用基础特征
                    basic_feat = np.concatenate([lbp_feat, hog_feat])
                    combined = np.pad(basic_feat, (0, max(0, 500 - len(basic_feat))), mode='constant')[:500]
                
                # 应用特征选择和标准化
                if hasattr(self, 'feature_selector') and self.feature_selector is not None:
                    combined_selected = self.feature_selector.transform([combined])
                else:
                    combined_selected = [combined]
                
                if hasattr(self, 'scaler') and self.scaler is not None:
                    combined_scaled = self.scaler.transform(combined_selected)
                else:
                    combined_scaled = combined_selected
                
                if hasattr(self, 'pca') and self.pca is not None:
                    features_processed = self.pca.transform(combined_scaled)
                    return features_processed[0]
                else:
                    return combined_scaled[0]
                    
            elif feature_method == "LBP特征":
                img = face_data.flatten()
                return self.extract_lbp_features(img)
                
            elif feature_method == "HOG特征":
                img = face_data.flatten()
                return self.extract_hog_features(img)
                
            elif feature_method == "简化CNN":
                img = face_data.flatten()
                cnn_features = self.extract_deep_cnn_features([img])[0]
                if hasattr(self, 'scaler') and self.scaler is not None:
                    features_scaled = self.scaler.transform([cnn_features])
                    return features_scaled[0]
                else:
                    return cnn_features
                    
            else:  # 原始像素
                return face_data.flatten()
                
        except Exception as e:
            self.log_message(f"特征提取失败: {str(e)}", "ERROR")
            # 返回原始数据作为备用
            return face_data.flatten()
    
    def save_face_database(self):
        """保存人脸数据库到文件"""
        try:
            db_dir = os.path.join(os.getcwd(), "face_database")
            if not os.path.exists(db_dir):
                os.makedirs(db_dir)
            
            db_file = os.path.join(db_dir, "face_database.pkl")
            with open(db_file, 'wb') as f:
                pickle.dump(self.face_database, f)
            
            self.log_message(f"人脸数据库已保存: {db_file}")
        except Exception as e:
            self.log_message(f"保存人脸数据库失败: {str(e)}")
    
    def load_face_database(self):
        """加载人脸数据库"""
        try:
            db_file = os.path.join(os.getcwd(), "face_database", "face_database.pkl")
            if os.path.exists(db_file):
                with open(db_file, 'rb') as f:
                    self.face_database = pickle.load(f)
                
                self.face_counter = len(self.face_database)
                self.log_message(f"已加载人脸数据库，包含 {len(self.face_database)} 个人脸")
                
                if len(self.face_database) > 0:
                    self.camera_button.config(state='normal')
                    
        except Exception as e:
            self.log_message(f"加载人脸数据库失败: {str(e)}")
    
    def manage_faces(self):
        """人脸管理界面"""
        try:
            if len(self.face_database) == 0:
                messagebox.showinfo("信息", "人脸数据库为空！\n请先录入一些人脸数据。")
                return
            
            # 创建人脸管理窗口
            manage_window = tk.Toplevel(self.root)
            manage_window.title("人脸管理")
            manage_window.geometry("1000x700")
            manage_window.configure(bg='#ffffff')
            manage_window.transient(self.root)
            manage_window.grab_set()
            
            # 居中显示
            manage_window.update_idletasks()
            x = (manage_window.winfo_screenwidth() // 2) - (1000 // 2)
            y = (manage_window.winfo_screenheight() // 2) - (700 // 2)
            manage_window.geometry(f"1000x700+{x}+{y}")
            
            # 标题
            title_label = tk.Label(manage_window, 
                                  text="👥 人脸数据库管理", 
                                  font=('等线', 16, 'bold'),
                                  bg='#ffffff', 
                                  fg='#0066cc')
            title_label.pack(pady=20)
            
            # 统计信息
            stats_frame = tk.Frame(manage_window, bg='#f8f9fa', relief='solid', bd=1)
            stats_frame.pack(fill='x', padx=20, pady=(0, 20))
            
            total_faces = len(self.face_database)
            stats_text = f"📊 统计信息: 共 {total_faces} 个人脸数据"
            stats_label = tk.Label(stats_frame,
                                  text=stats_text,
                                  font=('等线', 12),
                                  bg='#f8f9fa',
                                  fg='#212529')
            stats_label.pack(pady=10)
            
            # 创建主框架
            main_frame = tk.Frame(manage_window, bg='#ffffff')
            main_frame.pack(fill='both', expand=True, padx=20, pady=(0, 20))
            
            # 左侧人脸列表
            left_frame = tk.Frame(main_frame, bg='#ffffff')
            left_frame.pack(side='left', fill='both', expand=True, padx=(0, 15))
            
            list_title = tk.Label(left_frame,
                                 text="📋 人脸列表",
                                 font=('等线', 12, 'bold'),
                                 bg='#ffffff',
                                 fg='#0066cc')
            list_title.pack(anchor='w', pady=(0, 10))
            
            # 创建Treeview
            columns = ('姓名', '录入时间', '特征方法', '特征维度')
            tree = ttk.Treeview(left_frame, columns=columns, show='headings', height=15)
            
            # 设置列标题
            tree.heading('#1', text='姓名')
            tree.heading('#2', text='录入时间')
            tree.heading('#3', text='特征方法')
            tree.heading('#4', text='特征维度')
            
            # 设置列宽
            tree.column('#1', width=120, minwidth=100)
            tree.column('#2', width=150, minwidth=120)
            tree.column('#3', width=120, minwidth=100)
            tree.column('#4', width=100, minwidth=80)
            
            # 添加滚动条
            tree_scroll = ttk.Scrollbar(left_frame, orient="vertical", command=tree.yview)
            tree.configure(yscrollcommand=tree_scroll.set)
            
            tree.pack(side='left', fill='both', expand=True)
            tree_scroll.pack(side='right', fill='y')
            
            # 右侧详情和操作
            right_frame = tk.Frame(main_frame, bg='#f8f9fa', relief='solid', bd=1, width=350)
            right_frame.pack(side='right', fill='y')
            right_frame.pack_propagate(False)
            
            details_title = tk.Label(right_frame,
                                    text="📄 详细信息",
                                    font=('等线', 12, 'bold'),
                                    bg='#f8f9fa',
                                    fg='#0066cc')
            details_title.pack(pady=(15, 10))
            
            # 人脸图片显示
            image_frame = tk.Frame(right_frame, bg='#ffffff', relief='solid', bd=1)
            image_frame.pack(padx=15, pady=(0, 10))
            
            image_canvas = tk.Canvas(image_frame, 
                                   width=150, 
                                   height=150, 
                                   bg='#ffffff',
                                   highlightthickness=0)
            image_canvas.pack(padx=10, pady=10)
            
            # 添加占位文本
            image_canvas.create_text(75, 75, 
                                   text="👤\n选择人脸\n查看图片", 
                                   fill='#6c757d', 
                                   font=('等线', 10),
                                   justify='center')
            
            # 详情显示区域
            details_text = tk.Text(right_frame,
                                  height=12,
                                  width=40,
                                  bg='#ffffff',
                                  fg='#212529',
                                  font=('等线', 9),
                                  relief='solid',
                                  bd=1,
                                  state='disabled')
            details_text.pack(padx=15, pady=(0, 15))
            
            # 操作按钮
            button_frame = tk.Frame(right_frame, bg='#f8f9fa')
            button_frame.pack(fill='x', padx=15, pady=(0, 15))
            
            edit_btn = ttk.Button(button_frame,
                                 text="✏️ 编辑姓名",
                                 style='Modern.TButton')
            edit_btn.pack(fill='x', pady=(0, 8))
            
            delete_btn = ttk.Button(button_frame,
                                   text="🗑️ 删除人脸",
                                   style='Warning.TButton')
            delete_btn.pack(fill='x', pady=(0, 8))
            
            export_btn = ttk.Button(button_frame,
                                   text="📤 导出数据",
                                   style='Modern.TButton')
            export_btn.pack(fill='x')
            
            # 填充数据到Treeview
            face_data = {}
            for person_id, person_info in self.face_database.items():
                name = person_info.get('name', 'Unknown')
                timestamp = person_info.get('timestamp', 'Unknown')
                feature_method = person_info.get('feature_method', 'Unknown')
                features = person_info.get('features', [])
                feature_dim = len(features) if hasattr(features, '__len__') else 'Unknown'
                
                # 格式化时间显示
                if timestamp != 'Unknown':
                    try:
                        dt = datetime.strptime(timestamp, "%Y-%m-%d %H:%M:%S")
                        display_time = dt.strftime("%m-%d %H:%M")
                    except:
                        display_time = timestamp[:16] if len(timestamp) > 16 else timestamp
                else:
                    display_time = 'Unknown'
                
                item_id = tree.insert('', 'end', values=(
                    name,
                    display_time,
                    feature_method[:12] if feature_method != 'Unknown' else 'Unknown',
                    str(feature_dim)
                ))
                face_data[item_id] = (person_id, person_info)
            
            # 选择事件处理
            def on_item_select(event):
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    person_id, person_info = face_data[item_id]
                    
                    # 显示人脸图片
                    try:
                        if 'image' in person_info:
                            face_image = person_info['image']
                            # 转换为PIL图像
                            if face_image.max() <= 1.0:
                                face_image = (face_image * 255).astype(np.uint8)
                            
                            image_pil = Image.fromarray(face_image)
                            image_resized = image_pil.resize((140, 140), Image.LANCZOS)
                            photo = ImageTk.PhotoImage(image_resized)
                            
                            # 清空画布并显示图像
                            image_canvas.delete("all")
                            image_canvas.create_image(75, 75, image=photo)
                            # 保持引用避免被垃圾回收
                            image_canvas.image = photo
                        else:
                            image_canvas.delete("all")
                            image_canvas.create_text(75, 75, 
                                                   text="👤\n无图片", 
                                                   fill='#6c757d', 
                                                   font=('等线', 10),
                                                   justify='center')
                    except Exception as e:
                        self.log_message(f"显示人脸图片失败: {str(e)}", "WARNING")
                        image_canvas.delete("all")
                        image_canvas.create_text(75, 75, 
                                               text="👤\n图片错误", 
                                               fill='#dc3545', 
                                               font=('等线', 10),
                                               justify='center')
                    
                    # 更新详细信息
                    details_text.config(state='normal')
                    details_text.delete(1.0, tk.END)
                    
                    detail_info = f"👤 姓名: {person_info.get('name', 'Unknown')}\n\n"
                    detail_info += f"🆔 ID: {person_id}\n\n"
                    detail_info += f"⏰ 录入时间:\n{person_info.get('timestamp', 'Unknown')}\n\n"
                    detail_info += f"🔍 特征提取方法:\n{person_info.get('feature_method', 'Unknown')}\n\n"
                    detail_info += f"🤖 模型方法:\n{person_info.get('model_method', 'Unknown')}\n\n"
                    
                    # 特征信息
                    features = person_info.get('features', [])
                    if hasattr(features, '__len__'):
                        detail_info += f"📊 特征维度: {len(features)}\n"
                        if hasattr(features, 'shape'):
                            detail_info += f"📏 特征形状: {features.shape}\n"
                        if hasattr(features, 'dtype'):
                            detail_info += f"🔢 数据类型: {features.dtype}\n"
                    else:
                        detail_info += f"📊 特征信息: 无效\n"
                    
                    details_text.insert(1.0, detail_info)
                    details_text.config(state='disabled')
            
            tree.bind('<<TreeviewSelect>>', on_item_select)
            
            # 按钮事件处理
            def edit_name():
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    person_id, person_info = face_data[item_id]
                    
                    current_name = person_info.get('name', '')
                    new_name = simpledialog.askstring("编辑姓名", 
                        f"请输入新的姓名:", initialvalue=current_name)
                    
                    if new_name and new_name.strip() and new_name.strip() != current_name:
                        # 更新数据库
                        self.face_database[person_id]['name'] = new_name.strip()
                        
                        # 更新树形控件
                        current_values = list(tree.item(item_id, 'values'))
                        current_values[0] = new_name.strip()
                        tree.item(item_id, values=current_values)
                        
                        # 更新face_data
                        face_data[item_id] = (person_id, self.face_database[person_id])
                        
                        # 保存到文件
                        self.save_face_database()
                        
                        # 刷新详细信息
                        on_item_select(None)
                        
                        messagebox.showinfo("成功", f"已将姓名修改为: {new_name.strip()}")
                        self.log_message(f"修改人脸姓名: {person_id} -> {new_name.strip()}")
                else:
                    messagebox.showwarning("警告", "请选择一个人脸！")
            
            def delete_face():
                selection = tree.selection()
                if selection:
                    item_id = selection[0]
                    person_id, person_info = face_data[item_id]
                    
                    result = messagebox.askyesno("确认删除", 
                        f"确定要删除 {person_info.get('name', 'Unknown')} 的人脸数据吗？\n\n此操作不可撤销！")
                    
                    if result:
                        try:
                            # 从数据库删除
                            del self.face_database[person_id]
                            
                            # 从树形控件删除
                            tree.delete(item_id)
                            del face_data[item_id]
                            
                            # 保存到文件
                            self.save_face_database()
                            
                            # 更新统计信息
                            total_faces = len(self.face_database)
                            stats_text = f"📊 统计信息: 共 {total_faces} 个人脸数据"
                            stats_label.config(text=stats_text)
                            
                            # 清空详细信息
                            details_text.config(state='normal')
                            details_text.delete(1.0, tk.END)
                            details_text.config(state='disabled')
                            
                            # 清空图片
                            image_canvas.delete("all")
                            image_canvas.create_text(75, 75, 
                                                   text="👤\n选择人脸\n查看图片", 
                                                   fill='#6c757d', 
                                                   font=('等线', 10),
                                                   justify='center')
                            
                            # 如果数据库为空，禁用摄像头识别
                            if len(self.face_database) == 0:
                                self.camera_button.config(state='disabled')
                            
                            messagebox.showinfo("成功", "人脸数据已删除！")
                            self.log_message(f"已删除人脸: {person_id}")
                            
                        except Exception as e:
                            messagebox.showerror("错误", f"删除人脸数据失败: {str(e)}")
                            self.log_message(f"删除人脸失败: {str(e)}", "ERROR")
                else:
                    messagebox.showwarning("警告", "请选择一个人脸！")
            
            def export_data():
                try:
                    export_file = filedialog.asksaveasfilename(
                        title="导出人脸数据",
                        defaultextension=".txt",
                        filetypes=[("文本文件", "*.txt"), ("所有文件", "*.*")]
                    )
                    
                    if export_file:
                        with open(export_file, 'w', encoding='utf-8') as f:
                            f.write("AI人脸识别系统 - 人脸数据库导出\n")
                            f.write("=" * 50 + "\n\n")
                            f.write(f"导出时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
                            f.write(f"人脸总数: {len(self.face_database)}\n\n")
                            
                            for i, (person_id, person_info) in enumerate(self.face_database.items(), 1):
                                f.write(f"人脸 {i}:\n")
                                f.write(f"  ID: {person_id}\n")
                                f.write(f"  姓名: {person_info.get('name', 'Unknown')}\n")
                                f.write(f"  录入时间: {person_info.get('timestamp', 'Unknown')}\n")
                                f.write(f"  特征方法: {person_info.get('feature_method', 'Unknown')}\n")
                                f.write(f"  模型方法: {person_info.get('model_method', 'Unknown')}\n")
                                
                                features = person_info.get('features', [])
                                if hasattr(features, '__len__'):
                                    f.write(f"  特征维度: {len(features)}\n")
                                
                                f.write("\n")
                        
                        messagebox.showinfo("成功", f"人脸数据已导出到: {export_file}")
                        
                except Exception as e:
                    messagebox.showerror("错误", f"导出失败: {str(e)}")
            
            # 绑定按钮事件
            edit_btn.config(command=edit_name)
            delete_btn.config(command=delete_face)
            export_btn.config(command=export_data)
            
        except Exception as e:
            self.log_message(f"人脸管理界面出错: {str(e)}", "ERROR")
            messagebox.showerror("错误", f"打开人脸管理界面失败: {str(e)}")
    
    def camera_recognition(self):
        """摄像头实时识别 - 使用人脸数据库进行识别"""
        # 检查是否有人脸数据库
        if len(self.face_database) == 0:
            messagebox.showwarning("警告", "请先录入人脸数据到数据库！\n\n摄像头识别功能使用人脸数据库进行比对")
            return
            
        try:
            cap = cv2.VideoCapture(0)
            if not cap.isOpened():
                messagebox.showerror("错误", "无法打开摄像头!")
                return
            
            # 显示识别方法信息
            method_info = f"使用人脸数据库识别 ({len(self.face_database)} 个人脸)"
            self.log_message(f"识别方法: {method_info}")
            messagebox.showinfo("提示", f"开始识别！\n{method_info}\n识别频率：每0.3秒一次\n按 'q' 键退出识别模式")
            
            face_cascade = self.load_face_cascade()
            use_simple_mode = False
            if face_cascade is None:
                # 如果没有级联分类器，使用简单的备用方案
                result = messagebox.askyesno("检测器缺失", 
                    "人脸检测器加载失败，是否继续使用简单识别模式？\n(将使用摄像头中心区域进行识别)")
                if not result:
                    cap.release()
                    return
                use_simple_mode = True
                self.log_message("使用简单识别模式", "WARNING")
            
            # 性能优化：添加识别间隔控制
            last_recognition_time = 0
            recognition_interval = 0.3  # 300毫秒识别一次，减少资源占用
            cached_results = {}  # 缓存识别结果
            frame_count = 0
            
            # 多次验证机制：防止误识别
            verification_history = {}  # 存储每个人脸位置的识别历史
            verification_required = 3   # 需要连续3次识别成功才确认
            stable_results = {}         # 稳定的识别结果
            
            while True:
                ret, frame = cap.read()
                if not ret:
                    break
                
                frame_count += 1
                current_time = time.time()
                
                # 转换为灰度图
                gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                
                # 人脸检测（每帧都进行，但识别不是每帧都进行）
                if use_simple_mode:
                    # 简单模式：使用中心区域作为人脸
                    h, w = gray.shape
                    center_size = min(h, w) // 3
                    x = (w - center_size) // 2
                    y = (h - center_size) // 2
                    faces = [(x, y, center_size, center_size)]
                else:
                    # 正常模式：使用级联分类器检测人脸，添加更严格的参数和质量检查
                    try:
                        # 使用更严格的参数减少误检
                        raw_faces = face_cascade.detectMultiScale(
                            gray, 
                            scaleFactor=1.05,      # 降低缩放因子，提高精度
                            minNeighbors=6,        # 增加最小邻居数，减少误检
                            minSize=(40, 40),      # 设置最小人脸大小
                            maxSize=(300, 300),    # 设置最大人脸大小
                            flags=cv2.CASCADE_SCALE_IMAGE
                        )
                        
                        # 对检测到的人脸进行质量筛选
                        faces = self.filter_face_detections(gray, raw_faces)
                        
                    except Exception as e:
                        self.log_message(f"人脸检测错误: {str(e)}", "WARNING")
                        faces = []
                
                # 只在达到识别间隔时进行识别
                should_recognize = (current_time - last_recognition_time) >= recognition_interval
                
                for face_idx, (x, y, w, h) in enumerate(faces):
                    face_key = f"face_{face_idx}"
                    face_center = ((x + w//2), (y + h//2))  # 人脸中心点，用于追踪
                    
                    if should_recognize:
                        # 提取人脸区域
                        face_roi = gray[y:y+h, x:x+w]
                        face_resized = cv2.resize(face_roi, (64, 64))
                        face_normalized = face_resized.astype('float64') / 255.0
                        
                        # 进行识别
                        recognition_result = self.recognize_face(face_normalized.flatten())
                        
                        # 多次验证逻辑
                        if recognition_result['success']:
                            # 识别成功，记录到验证历史
                            if face_key not in verification_history:
                                verification_history[face_key] = []
                            
                            verification_history[face_key].append({
                                'name': recognition_result['name'],
                                'confidence': recognition_result['confidence'],
                                'time': current_time
                            })
                            
                            # 保持历史记录不超过验证要求数量
                            if len(verification_history[face_key]) > verification_required:
                                verification_history[face_key] = verification_history[face_key][-verification_required:]
                            
                            # 检查是否满足验证要求
                            recent_history = verification_history[face_key]
                            if len(recent_history) >= verification_required:
                                # 检查最近几次识别是否一致
                                recent_names = [h['name'] for h in recent_history[-verification_required:]]
                                if len(set(recent_names)) == 1:  # 所有名字都相同
                                    # 计算平均置信度
                                    avg_confidence = sum([h['confidence'] for h in recent_history[-verification_required:]]) / verification_required
                                    stable_results[face_key] = {
                                        'success': True,
                                        'name': recent_names[0],
                                        'confidence': avg_confidence,
                                        'method': '人脸数据库(已验证)',
                                        'verified': True
                                    }
                                else:
                                    # 识别结果不一致，继续验证
                                    stable_results[face_key] = {
                                        'success': False,
                                        'name': '验证中...',
                                        'confidence': recognition_result['confidence'],
                                        'method': '多次验证',
                                        'verified': False
                                    }
                            else:
                                # 还需要更多验证
                                stable_results[face_key] = {
                                    'success': False,
                                    'name': f'验证中({len(recent_history)}/{verification_required})',
                                    'confidence': recognition_result['confidence'],
                                    'method': '多次验证',
                                    'verified': False
                                }
                        else:
                            # 识别失败，清除历史记录
                            verification_history[face_key] = []
                            stable_results[face_key] = recognition_result
                        
                        cached_results[face_key] = stable_results.get(face_key, recognition_result)
                        
                        if should_recognize and face_idx == 0:  # 只对第一个人脸更新时间
                            last_recognition_time = current_time
                    else:
                        # 使用缓存的识别结果，如果没有缓存则显示检测中
                        recognition_result = cached_results.get(face_key, {
                            'success': False,
                            'name': '检测中...',
                            'confidence': 0,
                            'method': '等待识别'
                        })
                    
                    # 根据识别结果绘制边框和标签
                    if recognition_result['success']:
                        # 识别成功
                        if recognition_result.get('verified', False):
                            color = (0, 255, 0)  # 绿色 - 已验证
                        else:
                            color = (0, 255, 255)  # 青色 - 待验证
                        name = recognition_result['name']
                        confidence = recognition_result['confidence']
                        method = recognition_result['method']
                        label = f"{name} ({confidence:.2f})"
                        if should_recognize and recognition_result.get('verified', False):
                            self.log_message(f"识别到: {name} (置信度: {confidence:.3f}, 方法: {method})")
                    else:
                        # 未识别或检测中
                        if 'error' in recognition_result:
                            color = (0, 165, 255)  # 橙色 - 系统错误
                        elif '检测中' in recognition_result['name'] or '验证中' in recognition_result['name']:
                            color = (255, 255, 0)  # 黄色 - 等待识别/验证
                        else:
                            color = (0, 0, 255)  # 红色 - 未知人员
                        
                        confidence = recognition_result['confidence']
                        label = f"{recognition_result['name']} ({confidence:.2f})"
                    
                    cv2.rectangle(frame, (x, y), (x+w, y+h), color, 2)
                    
                    # 使用中文显示函数
                    try:
                        # 添加背景矩形
                        cv2.rectangle(frame, (x, y-35), (x + 200, y-5), color, -1)
                        # 使用中文显示
                        frame = self.put_chinese_text(frame, label, (x+5, y-30), 
                                                    font_size=16, color=(255, 255, 255))
                    except Exception as e:
                        # 备用英文显示
                        cv2.rectangle(frame, (x, y-30), (x + 150, y), color, -1)
                        cv2.putText(frame, label.encode('ascii', 'ignore').decode('ascii'), 
                                   (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
                
                # 显示性能信息
                fps_text = f"FPS: ~{1.0/max(0.001, recognition_interval):.1f} | Frame: {frame_count}"
                cv2.putText(frame, fps_text, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255), 2)
                
                # 清理过期的验证历史记录（每100帧清理一次）
                if frame_count % 100 == 0:
                    current_face_keys = [f"face_{i}" for i in range(len(faces))]
                    # 清理不再存在的人脸的历史记录
                    keys_to_remove = [k for k in verification_history.keys() if k not in current_face_keys]
                    for key in keys_to_remove:
                        del verification_history[key]
                        if key in stable_results:
                            del stable_results[key]
                        if key in cached_results:
                            del cached_results[key]
                
                cv2.imshow('人脸识别 - 按Q退出', frame)
                
                if cv2.waitKey(1) & 0xFF == ord('q'):
                    break
                    
            cap.release()
            cv2.destroyAllWindows()
            self.log_message("摄像头识别结束")
            
        except Exception as e:
            self.log_message(f"摄像头识别失败: {str(e)}")
            messagebox.showerror("错误", f"摄像头识别失败: {str(e)}")
    
    def put_chinese_text(self, img, text, position, font_size=20, color=(255, 255, 255)):
        """在OpenCV图像上显示中文文本"""
        try:
            # 将OpenCV图像转换为PIL图像
            img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
            draw = ImageDraw.Draw(img_pil)
            
            # 尝试加载中文字体
            try:
                # Windows系统字体路径
                font_paths = [
                    "C:/Windows/Fonts/msyh.ttc",  # 微软雅黑
                    "C:/Windows/Fonts/simhei.ttf",  # 黑体
                    "C:/Windows/Fonts/simsun.ttc",  # 宋体
                ]
                
                font = None
                for font_path in font_paths:
                    if os.path.exists(font_path):
                        font = ImageFont.truetype(font_path, font_size)
                        break
                
                if font is None:
                    font = ImageFont.load_default()
                    
            except Exception:
                font = ImageFont.load_default()
            
            # 绘制文本
            draw.text(position, text, font=font, fill=color)
            
            # 转换回OpenCV格式
            return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR)
            
        except Exception as e:
            self.log_message(f"中文显示错误: {str(e)}", "WARNING")
            # 如果失败，使用英文显示
            cv2.putText(img, text.encode('ascii', 'ignore').decode('ascii'), 
                       position, cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
            return img
    
    def recognize_face_for_image(self, face_data):
        """图片识别专用方法 - 只使用训练模型，与测试功能完全相同的算法，基于olivetti_faces_data数据库"""
        try:
            # 提取特征
            processed_features = self.extract_face_features(face_data)
            
            # 只使用训练好的机器学习模型（与测试功能完全相同）
            if hasattr(self, 'model') and self.model is not None and self.is_trained:
                try:
                    # 确保特征向量是正确的形状
                    if len(processed_features.shape) == 1:
                        features_for_prediction = processed_features.reshape(1, -1)
                    else:
                        features_for_prediction = processed_features
                    
                    # 使用模型预测（与测试功能完全相同的逻辑）
                    if hasattr(self.model, 'predict_proba'):
                        probabilities = self.model.predict_proba(features_for_prediction)
                        predicted_label = np.argmax(probabilities)
                        confidence = np.max(probabilities)
                    else:
                        predicted_label = self.model.predict(features_for_prediction)[0]
                        confidence = 1.0  # 与测试功能保持一致
                    
                    # 与测试功能完全一致：不设置置信度阈值，直接使用预测结果
                    person_name = f"人员 {predicted_label}"
                    
                    return {
                        'success': True,
                        'name': person_name,
                        'confidence': confidence,
                        'method': '机器学习模型',
                        'predicted_label': predicted_label
                    }
                        
                except Exception as e:
                    self.log_message(f"机器学习模型识别失败: {str(e)}", "ERROR")
                    return {
                        'success': False,
                        'name': '识别错误',
                        'confidence': 0,
                        'method': '机器学习模型',
                        'error': str(e)
                    }
            else:
                return {
                    'success': False,
                    'name': '无模型',
                    'confidence': 0,
                    'method': '无',
                    'error': '没有可用的训练模型'
                }
                
        except Exception as e:
            self.log_message(f"图片识别失败: {str(e)}", "ERROR")
            return {
                'success': False,
                'name': '识别错误',
                'confidence': 0,
                'method': '错误',
                'error': str(e)
            }

    def recognize_face(self, face_data):
        """摄像头识别专用方法 - 只使用人脸数据库进行识别"""
        try:
            # 提取特征
            processed_features = self.extract_face_features(face_data)
            self.log_message(f"摄像头识别特征维度: {processed_features.shape}", "DEBUG")
            
            # 只使用人脸数据库进行识别（摄像头识别专用）
            if len(self.face_database) > 0:
                similarities = []
                
                # 计算与所有已录入人脸的相似度
                self.log_message(f"开始计算与 {len(self.face_database)} 个人脸的相似度", "DEBUG")
                for person_id, person_data in self.face_database.items():
                    try:
                        similarity = self.calculate_similarity(processed_features, person_data['features'])
                        similarities.append({
                            'person_id': person_id,
                            'person_data': person_data,
                            'similarity': similarity
                        })
                        self.log_message(f"与 {person_data['name']} 的相似度: {similarity:.3f}", "DEBUG")
                    except Exception as e:
                        self.log_message(f"计算相似度失败 {person_id}: {str(e)}", "WARNING")
                        continue
                
                if similarities:
                    # 按相似度排序
                    similarities.sort(key=lambda x: x['similarity'], reverse=True)
                    best_match = similarities[0]
                    
                    # 设置更合理的相似度阈值 - 既要能识别本人，又要防止误识别
                    similarity_threshold = 0.65  # 适中的阈值，防止误识别
                    
                    # 计算相似度差异 - 如果最高相似度和次高相似度差异不大，可能是未知人员
                    if len(similarities) > 1:
                        similarity_gap = similarities[0]['similarity'] - similarities[1]['similarity']
                        gap_threshold = 0.1  # 要求有足够的相似度差异
                    else:
                        similarity_gap = 1.0  # 只有一个人的情况
                        gap_threshold = 0.0
                    
                    # 记录识别详情
                    self.log_message(f"最佳匹配: {best_match['person_data']['name']}, 相似度: {best_match['similarity']:.3f}", "DEBUG")
                    self.log_message(f"阈值检查: 相似度阈值={similarity_threshold}, 差异阈值={gap_threshold}", "DEBUG")
                    self.log_message(f"相似度差异: {similarity_gap:.3f}", "DEBUG")
                    
                    # 同时满足相似度和相似度差异要求
                    if (best_match['similarity'] > similarity_threshold and 
                        similarity_gap > gap_threshold):
                        self.log_message(f"识别成功: {best_match['person_data']['name']}", "SUCCESS")
                        return {
                            'success': True,
                            'name': best_match['person_data']['name'],
                            'confidence': best_match['similarity'],
                            'method': '人脸数据库',
                            'similarity': best_match['similarity'],
                            'similarity_gap': similarity_gap
                        }
                    else:
                        # 记录未通过阈值的原因
                        reason = []
                        if best_match['similarity'] <= similarity_threshold:
                            reason.append(f"相似度不足({best_match['similarity']:.3f}<={similarity_threshold})")
                        if similarity_gap <= gap_threshold:
                            reason.append(f"相似度差异不足({similarity_gap:.3f}<={gap_threshold})")
                        
                        self.log_message(f"识别失败: {', '.join(reason)}", "INFO")
                        
                        return {
                            'success': False,
                            'name': '未知人员',
                            'confidence': best_match['similarity'],
                            'method': '人脸数据库',
                            'similarity': best_match['similarity'],
                            'similarity_gap': similarity_gap,
                            'reason': reason
                        }
                else:
                    return {
                        'success': False,
                        'name': '无法计算相似度',
                        'confidence': 0,
                        'method': '人脸数据库',
                        'error': '所有相似度计算都失败'
                    }
            
            # 如果没有任何识别方法可用
            return {
                'success': False,
                'name': '无识别方法',
                'confidence': 0,
                'method': '无',
                'error': '没有可用的识别方法'
            }
            
        except Exception as e:
            self.log_message(f"人脸识别失败: {str(e)}", "ERROR")
            return {
                'success': False,
                'name': '识别错误',
                'confidence': 0,
                'method': '错误',
                'error': str(e)
            }
    
    def calculate_similarity(self, features1, features2):
        """计算两个特征向量的余弦相似度"""
        try:
            # 确保特征向量是numpy数组
            f1 = np.array(features1).flatten()
            f2 = np.array(features2).flatten()
            
            # 检查维度是否匹配
            if f1.shape != f2.shape:
                self.log_message(f"特征维度不匹配: {f1.shape} vs {f2.shape}", "ERROR")
                return 0
            
            # 计算余弦相似度
            dot_product = np.dot(f1, f2)
            norm1 = np.linalg.norm(f1)
            norm2 = np.linalg.norm(f2)
            
            if norm1 == 0 or norm2 == 0:
                self.log_message("特征向量范数为零", "WARNING")
                return 0
            
            similarity = dot_product / (norm1 * norm2)
            
            return max(0, similarity)  # 确保返回值不为负
            
        except Exception as e:
            self.log_message(f"计算相似度失败: {str(e)}", "ERROR")
            return 0
    
    def display_image(self, image):
        """在画布上显示图像"""
        # 将图像转换为PIL格式
        if image.max() <= 1.0:
            image = (image * 255).astype(np.uint8)
        
        image_pil = Image.fromarray(image)
        image_resized = image_pil.resize((280, 280), Image.LANCZOS)
        self.photo = ImageTk.PhotoImage(image_resized)
        
        # 清空画布并显示图像
        self.image_canvas.delete("all")
        self.image_canvas.create_image(150, 150, image=self.photo)
    
    def run(self):
        """运行应用程序"""
        self.root.mainloop()

if __name__ == "__main__":
    try:
        app = CNNFaceRecognizer()
        app.run()
    except Exception as e:
        print(f"程序运行出错: {str(e)}")
        input("按任意键退出...")
